Moose::Role - easy way to augment application of the role? - perl

I have a Moose::Role that I would like to call some extra subs on the class when that role is applied to the class.
Is there an easy way to modify what happens when the role is applied, without having to dig too much into Moose::Meta::Role type coding? Ideally, I'd just like to after 'apply' => ... to add the extra stuff.
Edit:
I'm specifically using this with a DBIx::Class::Core result definition to create something like a component that also modifies the constructor. I would just write it as a component if I could get at BUILDARGS and BUILD subs for the result, but I can't seem to do. So, instead of doing load_component, I doing with 'role', but some of the effects of the component are to add belongs_to relationships to the class. Hence, I was thinking the best way to do that is during application of the role to the class.

In a briefly-lived comment I referred you to this question, which discusses how to access the metaclass of the class the role is being applied to (e.g. so you can build onto the class conditionally). However, that's a really stinky use of MooseX::Role::Parameterized providing you that information, and it also won't work if the role is being applied to another role, not to a class.
As an alternative, you could write a sugar function which receives the meta information, and build onto the class in that way:
sub foo
{
my ($meta, %options) = #_;
# based on what is present in %options, add additional attributes...
$meta->add_attribute(...);
}
See Moose::Cookbook::Extending::Recipe4 for an example of writing sugar functions.

You could use a parameterized role. There is an example on how to access the consuming class in the tutorial. That being said, I would advise you to join the Moose and DBIx-Class IRC channels or mailing lists to look for best-practices in this regard.

What I found that works, is compact, and seems in keeping with intent in the docs is to use a trait to modify the meta role used by my particular role:
package DBIx::Class::Meta::Role::MyRole;
use Moose;
BEGIN { extends 'Moose::Meta::Role'; }
after 'apply' => sub {
## ..my mods to add extra relationships to DBIx::Class::Core result
};
no Moose;
package DBIx::Class::MyRole;
use Moose::Role -metaclass => 'DBIx::Class::Meta::Role::MyRole';

Related

Modifying attributes of a Moose::Role at runtime

I have a Moose::Role that contains a network client as an attribute:
package Widget;
use Moose::Role;
has 'network_thingy' => (
isa => Maybe[ThingyClient],
);
And of course, I have a couple concrete Moose classes which use this role:
package BlueWidget;
use Moose;
with 'Widget';
Now it comes to functional testing of the Widgets. We have the ability to create ThingyServer objects, and it would be much faster and overall excellent to directly use ThingyServer objects instead of spinning up a daemon and having a ThingyClient connect to it over the network. Since ThingyClient & ThingyServer conveniently have the exact same methods, this should be easily possible. But of course, Moose is demanding that I use a ThingyClient when the test eventually constructs a BlueWidget.
I did some research, and came across the Moose::Meta documentation. Seemed perfect! So here's the test code:
my $metarole = Moose::Meta::Role->initialize('Widget');
// first remove the old attribute
$metarole->remove_attribute('network_thingy');
I was going to add a new attribute, but I thought I'd check on the state of the role & class first. Now if I dump out the $metarole, it looks great. There's no network_thingy attribute anymore. But if I construct a BlueWidget class, or just peak inside the metaclass...
$metaclass = Moose::Meta::Class->initialize('BlueWidget');
diag Dumper ($metaclass);
... sure enough network_thingy is still there. This is not at all what I expected. How can I modify/remove/replace an attribute of the Widget role at runtime?
When a class consumes a role, attributes are copied from the role to the class. If you then change the attribute in the role, the copy in the class is unaffected.
So you would need to loop through the classes that have consumed the role, and change the attribute in each class. There's a consumers method in Moose::Meta::Role that could help you get a list of classes that have consumed the role, however it only covers classes that have directly consumed the role, and not, say, subclasses of those.
If the classes have been made immutable (__PACKAGE__->meta->make_immutable), you'll need to make them mutable again before you make modify the attribute.
Overall, it's probably a better idea to just alter the role module (i.e. edit the file); not attempt to tweak the attribute at run time. Maybe set isa to a duck_type type constraint?

How to write a Moose base class for roles with inheritable data?

I have a small (soon to be large) set of Moose roles that interact with a database. They have methods like create_foo_record, update_foo_record, delete_foo_record, etc. (They aren't simple wrapper for database access, as they do things like translate and normalize data, log actions, etc. In some cases, they may create and update related records too.)
This code is pretty generic, so rather than have two dozen modules of cut-and-paste code, I would like a base module that implements these methods, using configuration parameters telling them what tables, columns, normalization functions, etc.
My instinct is to write something "higher-order" that generates these functions, but I think it's more appropriate to do that the Moose way. (I am a Moose novice, however...)
The naive way would be to have the base class be a role, and it's methods take configuration arguments, but this seems messy.
Ideally, I'd like the base class to have private configuration that the roles that use it set up, something like
__PACKAGE__->config( foo => 'bar' );
but I am unsure how to do this using Moose. My attempts have gotten errors complaining about extending a class into a role. Or the config parameters turns out not to be private, and interferes with other roles or is being interfered with by the corresponding config in Catalyst controllers that use the roles.
If it's possible to make use of 'Catalyst::Component' in the base class but somehow extend that to roles, that would be ideal. But I don't know how to do that, or even if it is possible.
Thanks.
Leaving aside that this sounds an awful lot like DBIx::Class::SchemaLoader, if I understand you correctly you want...
A thing to store system configuration information.
It has defaults.
Those defaults can be overridden on a per class basis.
One way to do it is to create a role and take advantage of builder methods. The role defines the defaults via the builder methods, and each class can override.
use v5.10;
{
package MyConfig;
use Moose::Role;
has user => (
is => 'ro',
lazy => 1,
builder => '_build_user',
);
sub _build_user {
return "default_user";
}
}
{
package MyApp;
use Moose;
with 'MyConfig';
sub _build_user {
return "some_user";
}
}
{
package DefaultApp;
use Moose;
with 'MyConfig';
}
say MyApp->new->user; # some_user
say DefaultApp->new->user; # default_user
Another is to take advantage of attribute inheritance.
use v5.10;
{
package MyConfig;
use Moose::Role;
has user =>
is => 'ro',
lazy => 1,
default => "default_user",
;
}
{
package MyApp;
use Moose;
with 'MyConfig';
has '+user' =>
default => 'some_user'
;
}
{
package DefaultApp;
use Moose;
with 'MyConfig';
}
say MyApp->new->user; # some_user
say DefaultApp->new->user; # default_user
They're basically the same thing. The former is done in a more traditional OO style and is more mechanistic. The latter is done using a bit of Moose magic and is more declarative and compact. Each has minor advantages and disadvantages, use which ever is comfortable to you.
I think what I need are parameterized roles http://metacpan.org/pod/MooseX::Role::Parameterized

Where do new methods go?

Let's say I have an object, Car, and it has sets of methods that are... disparate? Maybe getting Blob-like? (as in the antipattern, "blob") These methods operate in distinct enough sets and don't really criss-cross functionally.
Car
# methods related to suburban driving
->foo1
->foo2
# methods related city driving
->bar3
->bar4
Now let's say I wanted to add "off road driving" as one of the cases that my car object can support. If I add methods to the Car object, isn't that making it more blob like?
Car (augmented)
# methods related to suburban driving
->foo1
->foo2
# methods related city driving
->bar3
->bar4
# methods related off road driving
->blah5
->blah6
Should I:
A: Subclass Car. I can make an OffRoadCar extends Car object. But what if Car and OffRoadCar share the same data/state, and only differ by methods? OffRoadCar only "acts" more specifically than car, but isn't described any more specifically nor does it have unique fields. So instantiating an OffRoadCar is meaningless.
OffRoadCar extends Car
# methods related off road driving
->blah5
->blah6
B: Consume Car with a Static Method. Like OffRoadAdventure->Embark(Car). So the OffRoadAdventure class takes a Car object and has its way with it.
In C# this would be called an extension method.
I guess put another way is what is the solution to the Blob antipattern? Is it subclassing? Is it extension methods?
Also, another reason I wanted to silo out these methods is what if their implementation incurs some cost, like the inclusion of other classes/packages? I wouldn't want users of the core class to constantly pay for something they would never use. I figure making the cost explicit for the minority is worth the savings for the majority. And it makes the dependency graph more precise (and not blob-like).
PS - Implementation language is Perl.
With Perl you should be taking a good look at Moose and roles.
package Suburban;
use Moose::Role;
requires 'wheels';
sub 'foo1' {
...
}
sub 'foo2' {
...
}
package City;
use Moose::Role;
requires 'wheels';
sub 'bar3' {
...
}
sub 'bar4' {
...
}
package Offroad;
use Moose::Role;
requires 'wheels';
sub 'blah5' {
...
}
sub 'blah6' {
...
}
package UltraCar;
use Moose;
with qw/ Offroad City Suburban /;
has 'wheels' => ( is => 'rw', isa => 'Int', default => 4 );
package RentalCar;
use Moose;
with qw/ City Suburban /;
has 'wheels' => ( is => 'rw', isa => 'Int', default => 4 );
package Tricycle;
use Moose;
with qw/ Offroad /;
has 'wheels' => ( is => 'rw', isa => 'Int', default => 3 );
You can even add and remove roles at runtime.
I think you are in the right track. An elegant solution is to have OffRoadCar inherit from Car and override its Drive() method. That way, you can say:
Car myCar = new OffRoadCar();
myCar.Drive();
This will call the overridden Drive() method in the subclass, and you don't end up with a gazillion methods in a single "blob". Here's how the Drive() method in OffRoadCar may look like:
#Override
public void Drive()
{
/* code */
}
Instead of subclassing the Car class, you could use some form of Decorator pattern.
Here, the base ORM class and the decorators (say OffRoadDecorator) implement some common interface, and the behaviour of each decorator can be dynamically added to the original class.
The key difference here is that multiple decorators can be used on the same Car class, and it's up to your implementation to say how those should interact. There's nothing to stop you decorating your class using the OffRoadDecorator as well as the SuburbanDecorator, and you can decide exactly what that means for the car within the decorators.
(P.S. sorry I don't provide any code examples; my Perl is simply terrible. Have a look at this page for some other examples.)
Suburban, City and Offroad Driving sound like algorithms. Strategy Pattern might help here with Context being Car.
Not really an answer Mark and I'm not sure how deeply you want to go into this, but you could look into the idea of Method Resolution Order. Instead of the standard depth first search for methods, using mro allows you to perform a breadth first search. See this page on CPAN and brian d foy's article Use the C3 method resolution order in multiple inheritance for more.

How do you name a class/package/module like

How would you name a package who's sole purpose was to extend another module so you could apply roles to it? I need a package that extends (sub classes) Template::Context with Moose So I can then create roles and traits to apply to it, but I don't know what to name this package (class). Any advice?
Since its Moose-specific role-ification, I'd have Moose in the name. Template::Context::Moosified. Or Template::Context::WithAntlers.
But having an intermediate subclass just so you can stick roles onto it is weird. You can skip that middleman and simply declare composed classes directly.
package Template::Context::ForBreakfast;
use Moose;
extends "Template::Context";
with "Bacon", "Eggs", "Toast";
The class name should fall out of the role composition.
I'm not sure this is approved but you can always try applying the Role directly.
package R;
use Moose::Role;
sub f { say 42 }
package main;
use URI;
R->meta->apply( Moose::Meta::Class->initialize( 'URI' ) );
URI->new->f
Granted this needs some sugaring up, has absolutely no guarantees to work long term, and is probably totally unsupported. This is however how the MOP unsugared effectively works.

Perl Moose Method Modifiers: Call 'around' before 'before' and 'after'

I'm using Moose and I need to wrap method calls in my project. It's important that my wrapping code be the most outer modifier. What I've done so far is put my method modifiers in a Moose Role and then applied that role at the end of my class like this:
use Moose::Util;
Moose::Util::apply_all_roles(__PACKAGE__->meta, ('App:Roles::CustomRole'));
__PACKAGE__->meta->make_immutable;
This allows me to be reasonably sure that my my role's modifiers are defined last, therefore giving me the correct behavior for "before" and "after." (The "before" and "after" in the role are called very first and very last.)
I originally thought this would be sufficient, but I now really need to wrap methods in a similar way with "around." Class::MOP, which Moose is built on, applies "around" modifiers very first, therefore they're called after "before" and before "after."
For more detail, here is the current calling order of my modifiers:
CUSTOM ROLE before
before 2
before 1
CUSTOM ROLE around
around
method
around
CUSTOM ROLE around
after 1
after 2
CUSTOM ROLE AFTER
I really need something like this:
CUSTOM ROLE before
CUSTOM ROLE around
before 2
before 1
around
method
around
after 1
after 2
CUSTOM ROLE around
CUSTOM ROLE AFTER
Any ideas on how to get my "around" modifier to be applied / called where I want it to? I know I could do some symbol table hacking (like Class::MOP is already doing) but I'd really rather not.
Simplest solution is to have CUSTOM ROLE define a method that calls the main method and then wrap that.
role MyRole {
required 'wrapped_method';
method custom_role_base_wrapper { $self->wrapped_method(#_) }
around custom_role_base_wrapper { ... }
before custom_role_base_wrapper { ... }
}
The problem you're having is that you're trying to have the CUSTOM ROLE around wrap something other than a method. Which is not what it is designed to do. Other than writing similar symbol table hackery like you've suggested (probably you could argue one of the Moose people into exposing an API in Class::MOP to help get there), the only other solution I can think of is the one above.
If you don't want the extra call stack frame that custom_role_base_wrapper will add, you should look at Yuval's Sub::Call::Tail or using goto to manipulate the call stack.
I'm fairly new to Moose, but why do you do this:
use Moose::Util;
Moose::Util::apply_all_roles(__PACKAGE__->meta, ('App:Roles::CustomRole'));
rather than simply this?
with 'App:Roles::CustomRole';
Regarding your question, it's a bit of a hack, but could you split your around method into before and after methods and apply the role at the end of your class definition (so it is applied in your desired order)? You could use private attributes to save state between the two methods if absolutely necessary.