This came up very recently in #moose as well.
>> I see the doc about apply_all_roles to apply a role to an instance, but is
>> there a way of removing or replacing a role?
Not easily, the concept is a dangerous one to begin with. Once
composed a role is *part of* a Class's definition. Further it makes
promises about the interface that a class provides. While I can think
of a half dozen ways to try to perform such a trick, none of them make
sense when you realize that Moose has several other features that
could provide a valid solution in nearly all cases. In this case, I
would recommend delegation.
>> I'm setting up a hierarchical data structure, with a class that defines the
>> general aspects of the data structure and sub-classes for the component
>> sub-structures. There will be methods for traversing the hierarchy.
>>
>> I want to have it possible for a particular node in the structure be in
>> various states that can be altered dynamically. Here's an example of the
>> sort of thing I want to do:
>>
>> my $tree = Class->create_from_XML( file=>$file, duration=>$duration );
>>
>> The duration parameter would be one of qw( fill lazy stream ).
>>
>>
>> - With 'fill', the XML file would be read and parsed and the entire
>> structure created.
>>
>>
>> - With 'lazy' or 'stream', the XML file would be opened and read
>> far enough to create the top level class element only.
>>
>>
>> The class would have methods for traversing the data structure. When a node
>> was created with 'lazy' or 'stream', it would not actually be complete
>> initially, but the first traversal method applied to it would cause the next
>> level of nodes to be created. (For 'stream', when the traversal is finished
>> with a particular node, the node is no longer retained.)
>>
>> What I was thinking of doing was to have traversal roles to define the
>> traversal methods, and have a traverse_from_XML role (and similar roles for
>> creation from other sources) that gets applied to the node initially, but
>> then when the traversal actually hits the node, the methods in this role
>> would read further into the XML input file, and then change the node so that
>> it no longer had the traverse_from_XML role, but instead a
>> traverse_loaded_node role would be applied to it. When the node traversal
>> was complete, the child contents would be deleted and the node would be
>> re-roled into a traverse_completed role that failed if a further traversal
>> was attempted before the parent node had finished being traversed.
I think a delegate would achieve the same result. If you have a set of
Traversal objects that all perform a common interface (which can be a
role) then you can simply swap the traversal object during your
streaming. Something similar to the following should do the trick.
package XML::Traversal;
use Moose::Role;
requires 'traverse';
package XML::Loader;
use Moose;
has traversal_engine => (
does => 'XML::Traversal',
is => 'ro',
writer => '_swap_traversal_engine',
handles => 'XML::Traversal',
default => sub { XML::TraversalEngine::XML->new($self) }
);
sub _swap_to_node_traversal {
$self->traversal_engine(XML::TraversalEngine::Node->new($self))
}
sub _swap_to_terminal_traversal {
$self->traversal_engine(XML::TraversalEngine::Termial->new($self))
}
package XML::TraversalEngine::Terminal;
use Moose;
with qw(XML::Traversal);
sub traverse { confess 'Already Have Terminal Traversal.' }
>> However, when I read about method conflicts, I suspect that this whole
>> approach is not going to work and that I need to accomplish my goal in a
>> different way.
>>
>> Do you have any suggestions?
I am curious as to what you're working on and if PRANG, XML::Toolkit,
or XML::Rabbit might be of use. I know XML::Toolkit the best of these
three and while it doesn't have lazy/stream parsing in the way you
define it here, I can see how to implement something like that with
some subclassing to set up a system similar to this. I suspect PRANG
could work the same way.
-Chris