Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mojo::Base::with_roles patch #1120

Merged
merged 1 commit into from Aug 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 27 additions & 0 deletions lib/Mojo/Base.pm
Expand Up @@ -15,6 +15,10 @@ use IO::Handle ();
my $NAME
= eval { require Sub::Util; Sub::Util->can('set_subname') } || sub { $_[1] };

# Role support requires Role::Tiny 2.000001+
use constant ROLES =>
!!(eval { require Role::Tiny; Role::Tiny->VERSION('2.000001'); 1 });

# Protect subclasses using AUTOLOAD
sub DESTROY { }

Expand Down Expand Up @@ -61,6 +65,8 @@ sub attr {
}
}

sub can_roles {ROLES}

sub import {
my $class = shift;
return unless my $flag = shift;
Expand Down Expand Up @@ -101,6 +107,11 @@ sub tap {
return $self;
}

sub with_roles {
return Role::Tiny->create_class_with_roles(@_) if (ROLES);
Carp::croak "Role::Tiny 2.000001+ is required for with_roles method";
}

1;

=encoding utf8
Expand Down Expand Up @@ -210,6 +221,13 @@ executed at accessor read time if there's no set value, and gets passed the
current instance of the object as first argument. Accessors can be chained, that
means they return their invocant when they are called with an argument.

=head2 can_roles

my $bool = Mojo::Base->can_roles();

True if L<Role::Tiny> 2.000001+ is installed, indicates that roles are supported in L<Mojo::Base>
derived classes.

=head2 new

my $object = SubClass->new;
Expand Down Expand Up @@ -238,6 +256,15 @@ spliced or tapped into) a chained set of object method calls.
# Inject side effects into a method chain
$object->foo('A')->tap(sub { say $_->foo })->foo('B');

=head2 with_roles

my $new_class = Class->with_roles('Foo::Role1', 'Bar::Role2');
my $object = $new_class->new();

Create and return a new class that extends the given class with the
list of roles composed in order, using L<Role::Tiny>'s method
C<create_class_with_roles>.

=head1 SEE ALSO

L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
Expand Down
2 changes: 2 additions & 0 deletions lib/Mojolicious/Command/version.pm
Expand Up @@ -16,6 +16,7 @@ sub run {
= Mojo::IOLoop::Client->can_socks ? $IO::Socket::Socks::VERSION : 'n/a';
my $tls = Mojo::IOLoop::TLS->can_tls ? $IO::Socket::SSL::VERSION : 'n/a';
my $nnr = Mojo::IOLoop::Client->can_nnr ? $Net::DNS::Native::VERSION : 'n/a';
my $roles = Mojo::Base->can_roles ? $Role::Tiny::VERSION : 'n/a';

print <<EOF;
CORE
Expand All @@ -27,6 +28,7 @@ OPTIONAL
IO::Socket::Socks 0.64+ ($socks)
IO::Socket::SSL 1.94+ ($tls)
Net::DNS::Native 0.15+ ($nnr)
Role::Tiny 2.000001+ ($roles)

EOF

Expand Down
59 changes: 59 additions & 0 deletions t/mojo/roles.t
@@ -0,0 +1,59 @@
use Mojo::Base -strict;

use Test::More;

BEGIN {
plan skip_all => 'Role::Tiny 2.000001+ required for this test!'
unless Mojo::Base->can_roles;
}

package Mojo::RoleTest::LOUD;
use Role::Tiny;

sub yell {'HEY!'}

requires 'name';

sub hello {
my ($self) = @_;
return $self->yell . ' ' . uc($self->name) . '!!!';
}

package Mojo::RoleTest::quiet;
use Role::Tiny;

requires 'name';

sub whisper {
my ($self) = @_;
return 'psst, ' . lc($self->name);
}

package Mojo::RoleTest::Base;
use Mojo::Base -base;

has name => 'bob';

sub hello {
my ($self) = shift;
return 'hello ' . $self->name;
}

package main;

my $obj = Mojo::RoleTest::Base->new(name => 'Ted');
is($obj->name, 'Ted', 'attr works');
is($obj->hello, 'hello Ted', 'class method');

my $obj2 = Mojo::RoleTest::Base->with_roles('Mojo::RoleTest::LOUD')->new;
is($obj2->hello, 'HEY! BOB!!!', 'method from role overrides base method');
is($obj2->yell, 'HEY!', 'new method from role');

my $obj3 = Mojo::RoleTest::Base->with_roles('Mojo::RoleTest::quiet',
'Mojo::RoleTest::LOUD')->new(name => 'Joel');
is($obj3->name, 'Joel', 'attr from base class');
is($obj3->whisper, 'psst, joel', 'method from role1');
is($obj3->hello, 'HEY! JOEL!!!', 'method override from role2');

done_testing();