ErikR
ErikR

Reputation: 52039

programmatically creating a Moose class at run time

I've been playing around with this code:

package Foo;
use Moose;

package main;

my $PACKAGE = "Foo";
{
  no strict 'refs';
  my $has = *{"${PACKAGE}::has"}{CODE};
  my $with = *{"${PACKAGE}::with"}{CODE};

  # Add a instance member to class $PACKAGE

  $has->("bar", is => "rw", required => 1);

  # Add a role to class $PACKAGE

  $with->("some::role");
}

# Create an instance of $PACKAGE:

$PACKAGE->new(); # error: attribute 'bar' is required means we were successful

This allows me to create a Moose class at run-time, i.e. add instance members to a class, add roles, etc.

My question is: how can I import Moose into package $PACKAGE?

I know I can do this with eval: eval "package $PACKAGE; use Moose"; but I'm wondering if there is a solution along the lines of Moose->import(... $PACKAGE ...).

i.e., a way without using eval. Or is there a completely different way of creating and modifying Moose classes at run time?

Upvotes: 3

Views: 2471

Answers (3)

phaylon
phaylon

Reputation: 1923

You probably want to take a look at Moose::Meta::Class and its create method:

my $class = Moose::Meta::Class->create('Foo',
  attributes => [attr => Moose::Meta::Attribute->new(is => 'ro'), ...],
  roles => [...],
  methods => {...},
  superclasses => [...],
);

# Edit: Adding an attribute and method modifiers:
$class->add_attribute(otherattr => (is => 'ro'));
$class->add_around_method_modifier(methodname => sub { ... });

Moose::Meta::Class is a subclass of Class::MOP::Class, so you might want to peek into that one as well. With the above, you can specify roles, superclasses, attributes and methods, or you can first create and then add them via the MOP; whatever fits best.

For the attributes you'll want the Moose kind, which means Moose::Meta::Attribute objects. The constructor to that object is basically the same as using has.

Upvotes: 19

ikegami
ikegami

Reputation: 385877

Call extends, with, has, before, after, around, override and augment in the Moose package instead of the ones exported by Moose, and pass the meta object of the class you are creating as an additional first argument.

use strict;
use warnings;
use feature qw( say );

use Moose qw( );

{ # Create MyClass on the fly.
   my $meta = Moose->init_meta( for_class => 'MyClass' );

   # Moose::with( $meta, 'MyRole' );

   Moose::has( $meta, foo => (
      is => 'ro',
   ));
}

say MyClass->new( foo => "Foo" )->foo;  # Foo

Upvotes: -5

Related Questions