The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use strict;
use warnings;

use Test::More tests => 26;
use Test::Exception;

use Perl6::MetaModel;

=pod

This is an example of Ruby-style singleton methods using the
Perl6::MetaModel. To the best of my knowledge, this is actually
how it is implemented in Ruby as well. 

Here is a link to a description of how this works in Ruby:

http://www.rubygarden.org/ruby?ClassMethods/Discussion

=cut

my $Foo = $::Class->new('$:name' => 'Foo');
$Foo->superclasses([ $::Object ]);
isa_ok($Foo, 'Class');

cmp_ok($Foo->class, '!=', $::Class, '... Foo.class is not Class (it is the eigenclass)');

$Foo->add_singleton_method('class_test' => ::make_method(sub { '$Foo::class_test' }));
is($Foo->class_test(), '$Foo::class_test', '... got the singleton class method');

my $eFoo = $Foo->class;
cmp_ok($Foo->class, '!=', $::Class, '... Foo.class is no longer Class');

$Foo->add_singleton_method('class_test2' => ::make_method(sub { '$Foo::class_test2' }));
is($Foo->class_test2(), '$Foo::class_test2', '... got the other singleton class method');

is($Foo->class, $eFoo, '... Foo.class is still eFoo (only one Eigenclass is created)');

my $foo = $Foo->new();
isa_ok($foo, 'Foo');

my $foo2 = $Foo->new();
isa_ok($foo2, 'Foo');

is($foo->class, $Foo, '... $foo.class is $Foo');
is($foo2->class, $Foo, '... $foo2.class is $Foo');

$foo->add_singleton_method('test' => ::make_method(sub { '$foo::test' }));
is($foo->test(), '$foo::test', '... the singleton method worked');

my $efoo = $foo->class;
cmp_ok($foo->class, '!=', $Foo, '... $foo.class is no longer $Foo');

is($foo2->class, $Foo, '... $foo2.class is still $Foo');

$foo->add_singleton_method('test2' => ::make_method(sub { '$foo::test2' }));
is($foo->test2(), '$foo::test2', '... the other singleton method worked');
is($foo->test(), '$foo::test', '... the first singleton method still worked');

is($foo->class, $efoo, '... $foo.class is still $eFoo (only one eigenclass is created)');

dies_ok {
    $foo2->test();
} '... the singleton method is only for $foo';

# test it again,...

my $Bar = class 'Bar' => {
    is => [ $::Object ],
    methods => {
        'baz' => sub { 'Bar::baz' }
    }
};
isa_ok($Bar, 'Class');

my $bar = $Bar->new();
isa_ok($bar, 'Bar');

my $bar2 = $Bar->new();
isa_ok($bar2, 'Bar');

is($bar->class, $Bar, '... $bar.class is Bar');
is($bar2->class, $Bar, '... $bar.class is Bar');

$bar->add_singleton_method('baz' => ::make_method(sub { '$bar::baz' }));

is($bar->baz, '$bar::baz', '... got the right singleton method, overriding the classes method');
is($bar2->baz, 'Bar::baz', '... but still got the right method from the classes');

cmp_ok($bar->class, '!=', $Bar, '... $bar.class is no longer Bar');
is($bar2->class, $Bar, '... $bar.class is Bar');