Basic Architecture
I built a information retrieval tool in perl, using Moose as framework.
I have a class hiearchy for plugins with Base as a common base class for plugins, from which access method specific plugins inherit (methods being HTTP, FTP, IMAP, ...).
From these child classes, the actual worker classes inherit (one plugin per data source).
I use Moose roles to compose source specific behaviour into the actual worker classes (like enabling support for SSL client certificates in HTTP sources).
Problem
One of the method specific classes (Base::A) requires a role R. The same role R is also used by role S, which then is used by a work class X, inheriting from Base::A.
My problem is that the method modifiers in R are applied twice to X. Is there a way to prevent Moose from applying method modifiers to class that are already applied to one of the parent classes?
Example
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
use v5.14;
{
package R;
use Moose::Role;
before 'bar' => sub { say "R::before'bar'()" }
}
{
package S;
use Moose::Role;
with 'R';
before 'bar' => sub { say "S::before'bar'()" }
}
{
package Base;
use Moose;
sub foo { say "Hello foo()"; }
}
{
package Base::A;
use Moose;
extends 'Base';
with 'R';
sub bar { $_[0]->foo(); say "Hello bar()"; }
}
{
package X;
use Moose;
extends 'Base::A';
with 'S';
}
package main;
my $a = X->new();
$a->bar();
Actual Output
S::before'bar'()
R::before'bar'()
R::before'bar'()
Hello bar()
Expected Output
The line R::before'bar'() should appear only once.
First of all, your example can be much simpler:
{
package R;
use Moose::Role;
before 'bar' => sub { say "R::before'bar'()" }
}
{
package Base;
use Moose;
with 'R';
sub foo { say "Hello foo()"; }
sub bar { $_[0]->foo(); say "Hello bar()"; }
}
{
package X;
use Moose;
extends 'Base';
with 'R';
}
package main;
X->new()->bar();
The output is:
R::before'bar'()
R::before'bar'()
Hello foo()
Hello bar()
Why
I agree that this is kinda unexpected, but it all makes sense if you think about it. Roles are not base classes, roles are not interfaces with implementation (see Java), roles are not even “mixins” in a Python sense of this word (in Python we actually do inherit from mixins, but this is just language limitations). Roles are just bunches of features (attributes, methods, modifiers etc) you apply to your class. This is one-time action. The class that has a role doesn't “remember” it, it's just being applied as the class is created. You don't inherit from roles, so you shouldn't expect Moose to implement some diamond to merge multiple applyings of the same role.
On the other hand, if you try to do with qw(R S); then R is surprisingly (or maybe not really) is applied only once.
What to do
Now to the actual question. Since you want your "befores" to override each other, you can just forgo using before at all and refactor it to a simple method (like you do in any other languages that don't support such modifiers):
sub bar {
my ($self) = #_;
$self->_before_bar_hook();
# ...
}
sub _before_bar_hook {}
Conclusion
Both before/after modifiers and roles are pretty advanced Moose features, and I'm not really surprised of some bizarre side effect (such that you've discovered). And though I believe my explanation is mostly correct I would not recommend to use something that requires such explanations.
Me personally avoid using before/after modifiers at all, since I prefer an explicit call of the hooks (as shown above).
You can use a parameterized role to prevent wrapping a sub:
#! /usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
{ package R;
use MooseX::Role::Parameterized;
parameter should_wrap_bar => (
isa => 'Bool',
default => 1,
);
role {
my ($param) = #_;
before 'bar' => sub { say "R::before'bar'()" }
if $param->{should_wrap_bar};
};
}
{ package Base;
use Moose;
with 'R';
sub foo { say "Hello foo()"; }
sub bar { $_[0]->foo(); say "Hello bar()"; }
}
{ package X;
use Moose;
extends 'Base';
with R => { should_wrap_bar => 0 };
}
package main;
X->new->bar;
Related
I have noticed some Perl modules use a class based structure, but don't manage any data. The class is simply used to access the methods within and nothing more.
Consider the following example:
Class.pm
package Class;
use Moose;
sub do_something {
print "Hi!\n";
}
1;
test.pl
use Class;
# Instantiate an object from the class
my $obj = Class->new();
$obj->do_something();
In this example you can see that you would first instantiate an instance of the class, then call the method from the created object.
The same end result can be achieved like so:
Module.pm
package Module;
use strict;
use warnings;
sub do_something {
print "Hi!\n";
}
1;
test.pl
use Module;
Module::do_something();
I am wondering why people write modules using the first approach, and if there is some benefit that it provides. To me it seems like it adds an extra step, because in order to use the methods, you first need to instantiate an object of the class.
I don't understand why people would program like this unless it has some benefit that I am not seeing.
One benefit is inheritance. You can subclass behavior of an existing class if it supports the -> style subroutine calls (which is a weaker statement than saying the class is object-oriented, as I said in a comment above).
package Class;
sub new { bless \__PACKAGE__,__PACKAGE__ }
sub do_something { "foo" }
sub do_something_else { 42 }
1;
package Subclass;
#Sublcass::ISA = qw(Class);
sub new { bless \__PACKAGE__,__PACKAGE__ }
sub do_something_else { 19 }
package main;
use feature 'say';
$o1 = Class->new;
$o2 = Subclass->new;
say $o1->do_something; # foo
say $o2->do_something; # foo
say $o1->do_something_else; # 42
say $o2->do_something_else; # 19
A prominent use of this technique is the UNIVERSAL class, that all blessed references implicitly subclass. The methods defined in the UNIVERSAL namespace generally take a package name as the first argument (or resolve a reference in the first argument to its package name), are return some package information. The DB class also does something like this (though the DB package also maintains plenty of state).
There seem to be a number of ways to do polymorphism in Perl but they all feel "hacky" to me. I'm somewhat new to Perl so I could just be interpreting it wrong but I find most examples make your code too illegible and are controversial.
Say you have a widget class that contains some data and methods that all widgets need. There are also a handful of widget types (i.e. calender, schedule, etc). And they need to communicate with each other (via a parent container class).
Would playing with the namespace and making widget a prototype be worth it?
Should I give each widget a reference to an object (one of the types) at instantiation?
Just forget about types being objects and make widget a large class where only a few methods get used per instance based on what type is set.
Something else?
I come from a C/C++ background and I'm finding it difficult to decide on a Perl programming model.
Also, I'm don't have strong type safety or private member requirements. The project is a mid sized web app with a couple developers and portability to other projects isn't a priority. Easy extensibilty without needing to decipher Perl hacks would be useful though.
The "Modern Perl" approach is probably to define Widget as a role. A role can be thought of as similar to a mixin, an interface, or an abstract base class. Do this with Moose::Role or one of its more light-weight alternatives (Moo::Role, Role::Tiny).
{
package Widget;
use Moo::Role;
sub some_common_method {
my $self = shift;
...;
}
sub another_common_method {
my $self = shift;
...;
}
# Here we're indicating that all Widgets must
# have a method called yet_another_common_method,
# but we're not defining how that method should
# be implemented.
requires "yet_another_common_method";
}
Now you can create a class that composes that role:
{
package Calendar;
use Moo;
with "Widget";
# Here's a member variable.
has year => (is => "ro", required => 1);
# Widget requires us to implement this.
sub yet_another_common_method {
my $self = shift;
...;
}
# We can override Widget's implementation
# of this method.
sub some_common_method {
my $self = shift;
...;
}
# We can install hooks ("method modifiers")
# to Widget's methods.
before another_common_method => sub {
my $self = shift;
print STDERR "Calendar ", $self->year, ": another_common_method() was called.\n";
};
}
And another:
{
package Schedule;
use Moo;
with "Widget", "Editable";
sub yet_another_common_method {
my $self = shift;
...;
}
}
And use the classes:
my $calendar = Calendar->new( year => 2014 );
my $schedule = Schedule->new;
my #widgets = ($calendar, $schedule);
for (#widgets) {
$_->some_common_method if $_->does('Widget');
}
Trying to understand Moose:
use Modern::Perl;
package FOO {
use Moose;
sub rep { say " <report></report>"; }
sub doc {
say "<document>";
inner();
say "</document>";
}
}
package BAR {
use Moose;
extends 'FOO';
around 'rep' => sub {
my $orig = shift;
my $self = shift;
say "<document>";
$self->$orig(#_);
say "</document>";
};
augment 'doc' => sub {
say " <report></report>";
};
}
package main {
BAR->new->rep;
say "===";
BAR->new->doc;
}
Produces...
<document>
<report></report>
</document>
===
<document>
<report></report>
</document>
... the same result. When desinging the "model (object hierarchy)" - based on what I should decide when to use around and when augment?
Here are probably other (deeper) things what i currently didn't understand yet.
Can please someone provide an "more deep" explanation, because reading tru the Moose/Manual/MethodModifiers obviously not helped enough...
augment and around do rather different things. augment is designed to make this sort of pattern easier:
package Document {
use Moose;
sub make_document {
my $self = shift;
return "<doc>" . $self->_document_innards . "</doc>"
}
# stub; override in child class
sub _document_innards {
my $self = shift;
return "";
}
}
package Invoice {
use Moose;
extends 'Document';
sub _document_innards {
my $self = shift;
return "Give me money!";
}
}
With augment it becomes:
package Document {
use Moose;
sub make_document {
my $self = shift;
return "<doc>" . inner() . "</doc>"
}
}
package Invoice {
use Moose;
extends 'Document';
augment make_document => sub {
my $self = shift;
return "Give me money!";
};
}
On the other hand, around is used as a replacement for doing $self->SUPER::method(#args) because SUPER can't work in roles (the notion of which package to check superclasses for is bound at compile-time, so $self->SUPER::method(#args) would check superclasses of the role (i.e. none) instead of superclasses of the class that consumed the role. If you're not using roles, then SUPER can still be used in Moose classes just fine. TLDR: SUPER is broken by roles, so Moose gives you around as an alternative.
Another thing to compare is override which is a bit like around, but gives you this super() function which is perhaps slightly cleaner than $self->$orig(#_). It also has an "there can be only one" feature. If two roles try to provide an around modifier for the same method, that's fine: they both get to wrap the method (though the order in which they are applied is undefined). If two roles try to provide an override modifier, that's an error.
The implementation of augment is somewhat fragile in my experience, so that in my book is a reason to avoid it. Don't try to replace it with around, because they do rather different things. Instead, replace it with the pattern used in my first example above.
Using around should always be your first instinct. As (Moose creator) Stevan Little says about augment:
Thankfully, only a small percentage of people actually grok this
feature and of those people only a handful of them are crazy enough to
try and use it.
I have a Moose BaseDBModel which has different subclasses mapping to my tables in the database. All the methods in the subclasses are like "get_xxx" or "update_xxx" which refers to the different DB operations.
Now i want to implement a cache system for all these methods, so my idea is "before" all methods named like "get_xxx", I will search the name of the method as key in my memcache pool for value. If i found the value, then I will return the value directly instead of method.
ideally, my code is like this
BaseDBModel
package Speed::Module::BaseDBModel;
use Moose;
sub BUILD {
my $self = shift;
for my $method ($self->meta->get_method_list()){
if($method =~ /^get_/){
$self->meta->add_before_method_modifier($method,sub {
warn $method;
find_value_by_method_name($method);
[return_value_if_found_value]
});
}
}
}
SubClasses Example 1
package Speed::Module::Character;
use Moose;
extends 'Speed::Module::BaseDBModel';
method get_character_by_id {
xxxx
}
Now my problem is that when my program is running, it's repeatedly modify the methods, for example:
restart apache
visit the page which will call get_character_by_id, so I can see one warning message
Codes:
my $db_character = Speed::Module::Character->new(glr => $self->glr);
$character_state = $db_character->get_character_by_id($cid);
Warnings:
get_character_by_id at /Users/dyk/Sites/speed/lib/Speed/Module/BaseDBModel.pm line 60.
but if I refresh the page, I saw 2 warning messages
Warnings:
get_character_by_id at /Users/dyk/Sites/speed/lib/Speed/Module/BaseDBModel.pm line 60.
get_character_by_id at /Users/dyk/Sites/speed/lib/Speed/Module/BaseDBModel.pm line 60.
I am using mod_perl 2.0 with apache, every time i refresh the page, my get_character_by_id method will be modified which I don't want
Isn't your BUILD doing the add_before every time you construct a new instance? I'm not sure that's what you want.
Well, the simple/clunky way would be to set some package-level flag so you only do it once.
Otherwise, I think you want to hook into Moose's own attribute building. Have a look at this: http://www.perlmonks.org/?node_id=948231
The problem is BUILD runs every time your create an object (i.e. after every ->new() call), but add_before_method_modifier adds modifier to class, i.e. to all objects.
Simple solution
Mind, that use calls import function from used package every time. That is the place where you want to add modifiers.
Parent:
package Parent;
use Moose;
sub import {
my ($class) = #_;
foreach my $method ($class->meta->get_method_list) {
if ($method =~ /^get_/) {
$class->meta->add_before_method_modifier($method, sub {
warn $method
});
}
}
}
1;
Child1:
package Child1;
use Moose;
extends 'Parent';
sub get_a { 'a' }
1;
Child2:
package Child2;
use Moose;
extends 'Parent';
sub get_b { 'b' }
1;
So now it works as expected:
$ perl -e 'use Child1; use Child2; Child1->new->get_a; Child2->new->get_b; Child1->new->get_a;'
get_a at Parent.pm line 11.
get_b at Parent.pm line 11.
get_a at Parent.pm line 11.
Cleaner solution
Since you can't be 100% sure import will be called (since you can't be sure use will be used) the more cleaner and straightforward solution is just add something like use My::Getter::Cacher in every derived class.
package My::Getter::Cacher;
sub import {
my $class = [caller]->[0];
# ...
}
In this case every derived class should contain both extends 'Parent' and use My::Getter::Cacher since the first line is about inheritance while the second is about adding before modifier. You may count it a bit redundant, but as I said I believe it's more cleaner and straightforward.
P. S.
Maybe you should give a glance at Memoize module.
Is it possible to replace a method of a Moose object at runtime ?
By looking at the source code of Class::MOP::Method (which Moose::Meta::Method inherits from) I concluded that by doing
$method->{body} = sub{ my stuff }
I would be able to replace at runtime a method of an object.
I can get the method using
$object->meta->find_method_by_name(<method_name>);
However, this didn't quite work out.
Is it conceivable to modify methods at run time? And, what is the way to do it with Moose?
Moose or not, that does not sound like a good idea.
Instead, design your object to have an accessor for the method. For example, users of your class can use My::Frobnicator->frobnicator->() to get and invoke the frobnicator method and use My::Frobnicator->frobnicator(sub { } ) to set it.
Sinan's idea is a great start.
But with an little extra tweak, you can make using your method accessor just like using a normal method.
#!/usr/bin/perl
use strict;
use warnings;
use Carp;
my $f = Frob->new;
$f->frob(
sub {
my $self = shift;
print "$self was frobbed\n";
print Carp::longmess('frob')
}
);
print "\nCall frob as normal sub\n";
$f->frobit;
print "\nGoto frob\n";
$f->goto_frob;
BEGIN {
package Frob;
use Moose;
has 'frob' => (
is => 'rw',
isa => 'CodeRef',
);
sub frobit {
&{$_[0]->frob};
}
sub goto_frob {
goto $_[0]->frob;
}
}
The two methods in Frob are very similar.
frobit passes all arguments, including the invocant to the code ref.
goto_frob passes all arguments, including the invocant to the code ref, and replaces goto_frob's stack frame with the code refs.
Which to use depends on what you want in the stack.
Regarding munging the body storage of a Class::MOP::Method object, like so $method->{body} = sub { 'foo' }:
It's never a good idea to violate encapsulation when you are doing OOP. Especially not when you are working with complex object systems like Moose and Class::MOP. It's asking for trouble. Sometimes, there is no other way to get what you want, but even then, violating encapsulation is still a bad idea.
Using the previously mentioned MooseX::SingletonMethod you can replace an objects method.
For example:
{
package Foo;
use MooseX::SingletonMethod;
sub foo { say 'bar' };
}
my $bar = Foo->new;
my $baz = Foo->new;
# replace foo method just in $baz object
$baz->add_singleton_method( foo => sub { say 'baz' } );
$bar->foo; # => bar
$baz->foo; # => baz
Also see this SO answer to What should I do with an object that should no longer be used in Perl?, which shows how this can be achieved using Moose roles.
/I3az/