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.
Related
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?
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
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.
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';
After creating a metaclass using Moose::Meta::Class->create, how do I instantiate a real Moose class with that class as a metaclass?
(I need to create the metaclass also because I also want to apply some roles to it.)
The metaclass is the class, of course. If you want an instance of that class, just do:
my $instance = $meta->name->new
You might also need to make sure that $meta doesn't get collected too soon. Generally, you do this:
$meta->add_method( meta => sub { $meta } );
That will keep the metaclass around, but you're going to leak the class if you aren't careful. If you only do this once, it won't matter; if you do it thousands of times, you could get yourself into trouble.
Much better to use something higher-level like Moose::Meta::Class::create_anon_class or MooseX::Traits.
Not sure this answers this or your other SO question How do I build a Moose class at runtime, add a method to it, apply a role to it and instantiate it once? How would you approach this? at Building a Moose class at runtime and tuning it but have a look at:
MooseX::SingletonMethod
It may do what you want. Or you may find it useful to peer into our it works.
The documentation does provide links to blog posts I made while coming to grips with building this module so you may find those helpful also.
Here is an brief code example of MooseX::SingletonMethod:
{
package Foo;
use MooseX::SingletonMethod;
sub bar { say 'bar' }
}
my $baz = Foo->new;
my $bar = Foo->new;
$baz->add_singleton_method( baz => sub { say 'baz' } );
$baz->bar; # => bar
$bar->bar; # => bar
$baz->baz; # => baz
$bar->baz; # Throws can't find baz error
/I3az/