$self->{is_immutable}++;
- $self->{strict_constructor} = $args{strict_constructor};
-
if ($args{inline_constructor}) {
$self->add_method($args{constructor_name} =>
Mouse::Util::load_class($self->constructor_class)
my @res;
my $has_triggers;
- my $strict = $metaclass->__strict_constructor;
+ my $strict = $metaclass->strict_constructor;
if($strict){
push @res, 'my $used = 0;';
if($strict){
push @res, q{if($used < keys %{$args})}
- . sprintf q{{ %s->_report_unknown_args($metaclass, \@attrs, $args) }}, $method_class;
+ . q{{ $metaclass->_report_unknown_args(\@attrs, $args) }};
}
if($metaclass->is_anon_class){
return join "\n", @code;
}
-sub _report_unknown_args {
- my(undef, $metaclass, $attrs, $args) = @_;
-
- my @unknowns;
- my %init_args;
- foreach my $attr(@{$attrs}){
- my $init_arg = $attr->init_arg;
- if(defined $init_arg){
- $init_args{$init_arg}++;
- }
- }
-
- while(my $key = each %{$args}){
- if(!exists $init_args{$key}){
- push @unknowns, $key;
- }
- }
-
- $metaclass->throw_error( sprintf
- "Unknown attribute passed to the constructor of %s: %s",
- $metaclass->name, Mouse::Util::english_list(@unknowns),
- );
-}
-
1;
__END__
my @triggers_queue;
+ my $used = 0;
+
foreach my $attribute ($self->get_all_attributes) {
my $init_arg = $attribute->init_arg;
my $slot = $attribute->name;
if ($attribute->has_trigger) {
push @triggers_queue, [ $attribute->trigger, $object->{$slot} ];
}
+ $used++;
}
else { # no init arg
if ($attribute->has_default || $attribute->has_builder) {
}
}
+ if($used < keys %{$args} && $self->strict_constructor) {
+ $self->_report_unknown_args([ $self->get_all_attributes ], $args);
+ }
+
if(@triggers_queue){
foreach my $trigger_and_value(@triggers_queue){
my($trigger, $value) = @{$trigger_and_value};
sub is_immutable { $_[0]->{is_immutable} }
-sub __strict_constructor{ $_[0]->{strict_constructor} }
+sub strict_constructor{
+ my $self = shift;
+ if(@_) {
+ $self->{strict_constructor} = shift;
+ }
+
+ foreach my $class($self->linearized_isa) {
+ my $meta = Mouse::Util::get_metaclass_by_name($class)
+ or next;
+
+ if(exists $meta->{strict_constructor}) {
+ return $meta->{strict_constructor};
+ }
+ }
+
+ return 0; # false
+}
+
+sub _report_unknown_args {
+ my($metaclass, $attrs, $args) = @_;
+
+ my @unknowns;
+ my %init_args;
+ foreach my $attr(@{$attrs}){
+ my $init_arg = $attr->init_arg;
+ if(defined $init_arg){
+ $init_args{$init_arg}++;
+ }
+ }
+
+ while(my $key = each %{$args}){
+ if(!exists $init_args{$key}){
+ push @unknowns, $key;
+ }
+ }
+
+ $metaclass->throw_error( sprintf
+ "Unknown attribute passed to the constructor of %s: %s",
+ $metaclass->name, Mouse::Util::english_list(@unknowns),
+ );
+}
package Mouse::Meta::Role;
#define no_mro_get_linear_isa
#define mro_get_linear_isa(stash) mouse_mro_get_linear_isa(aTHX_ stash)
AV* mouse_mro_get_linear_isa(pTHX_ HV* const stash);
+#define mro_method_changed_in(stash) ((void)((stash), ++PL_sub_generation))
#endif /* !mro_get_linear_isa */
#ifndef mro_get_pkg_gen
use if 'Mouse' eq 'Moose',
'Test::More' => skip_all => 'Moose does nots support strict constructor';
use Test::More;
+use Test::Mouse;
use Test::Exception;
{
default => 42,
);
- __PACKAGE__->meta->make_immutable(strict_constructor => 1);
+ __PACKAGE__->meta->strict_constructor(1);
+}
+{
+ package MySubClass;
+ use Mouse;
+ extends 'MyClass';
}
-lives_and {
- my $o = MyClass->new(foo => 1);
- isa_ok($o, 'MyClass');
- is $o->baz, 42;
-} 'correc use of the constructor';
-
-lives_and {
- my $o = MyClass->new(foo => 1, baz => 10);
- isa_ok($o, 'MyClass');
- is $o->baz, 10;
-} 'correc use of the constructor';
-
-
-throws_ok {
- MyClass->new(foo => 1, hoge => 42);
-} qr/\b hoge \b/xms;
-
-throws_ok {
- MyClass->new(foo => 1, bar => 42);
-} qr/\b bar \b/xms, "init_arg => undef";
-
-
-throws_ok {
- MyClass->new(aaa => 1, bbb => 2, ccc => 3);
-} qr/\b aaa \b/xms, $@;
-
-throws_ok {
- MyClass->new(aaa => 1, bbb => 2, ccc => 3);
-} qr/\b bbb \b/xms, $@;
-
-throws_ok {
- MyClass->new(aaa => 1, bbb => 2, ccc => 3);
-} qr/\b ccc \b/xms, $@;
+with_immutable sub {
+ lives_and {
+ my $o = MyClass->new(foo => 1);
+ isa_ok($o, 'MyClass');
+ is $o->baz, 42;
+ } 'correc use of the constructor';
+
+ lives_and {
+ my $o = MyClass->new(foo => 1, baz => 10);
+ isa_ok($o, 'MyClass');
+ is $o->baz, 10;
+ } 'correc use of the constructor';
+
+
+ throws_ok {
+ MyClass->new(foo => 1, hoge => 42);
+ } qr/\b hoge \b/xms;
+
+ throws_ok {
+ MyClass->new(foo => 1, bar => 42);
+ } qr/\b bar \b/xms, "init_arg => undef";
+
+
+ eval {
+ MyClass->new(aaa => 1, bbb => 2, ccc => 3);
+ };
+ like $@, qr/\b aaa \b/xms;
+ like $@, qr/\b bbb \b/xms;
+ like $@, qr/\b ccc \b/xms;
+
+ eval {
+ MySubClass->new(aaa => 1, bbb => 2, ccc => 3);
+ };
+ like $@, qr/\b aaa \b/xms;
+ like $@, qr/\b bbb \b/xms;
+ like $@, qr/\b ccc \b/xms;
+}, qw(MyClass MySubClass);
done_testing;
flags |= MOUSEf_XC_HAS_BUILDARGS;
}
- if(predicate_calls(metaclass, "__strict_constructor")){
+ if(predicate_calls(metaclass, "strict_constructor")){
flags |= MOUSEf_XC_CONSTRUCTOR_IS_STRICT;
}
I32 const len = AvFILLp(attrs) + 1;
I32 i;
AV* triggers_queue = NULL;
- I32 used = 0;
+ U32 used = 0;
assert(meta || object);
assert(args);
INSTALL_SIMPLE_PREDICATE_WITH_KEY(Class, is_anon_class, anon_serial_id);
INSTALL_SIMPLE_READER(Class, is_immutable);
- INSTALL_SIMPLE_READER_WITH_KEY(Class, __strict_constructor, strict_constructor);
-
INSTALL_CLASS_HOLDER(Class, method_metaclass, "Mouse::Meta::Method");
INSTALL_CLASS_HOLDER(Class, attribute_metaclass, "Mouse::Meta::Attribute");
INSTALL_CLASS_HOLDER(Class, constructor_class, "Mouse::Meta::Method::Constructor::XS");
mouse_class_initialize_object(aTHX_ meta, object, args, is_cloning);
}
+void
+strict_constructor(SV* self, SV* value = NULL)
+CODE:
+{
+ SV* const slot = sv_2mortal(newSVpvs_share("strict_constructor"));
+ SV* const stash_ref = mcall0(self, mouse_namespace);
+ HV* stash;
+
+ if(!(SvROK(stash_ref) && SvTYPE(SvRV(stash_ref)) == SVt_PVHV)) {
+ croak("namespace() didn't return a HASH reference");
+ }
+ stash = (HV*)SvRV(stash_ref);
+
+ if(value) { /* setter */
+ set_slot(self, slot, value);
+ mro_method_changed_in(stash);
+ }
+
+ value = get_slot(self, slot);
+
+ if(!value) {
+ AV* const isa = mro_get_linear_isa(stash);
+ I32 const len = av_len(isa) + 1;
+ I32 i;
+ for(i = 1; i < len; i++) {
+ SV* const klass = MOUSE_av_at(isa, i);
+ SV* const meta = get_metaclass(klass);
+ if(!SvOK(meta)){
+ continue; /* skip non-Mouse classes */
+ }
+ value = get_slot(meta, slot);
+ if(value) {
+ break;
+ }
+ }
+ }
+ ST(0) = value ? value : &PL_sv_no;
+}
+
MODULE = Mouse PACKAGE = Mouse::Meta::Role
BOOT: