1 package LaTeX::TikZ::Functor;
8 LaTeX::TikZ::Functor - Build functor methods that recursively visit nodes of a LaTeX::TikZ::Set tree.
16 our $VERSION = '0.02';
20 A functor takes a L<LaTeX::TikZ::Set> tree and returns a new, transmuted version of it according to certain rules.
21 It recursively visits all the nodes of the tree, building a new set out of the result of the functor on the child sets.
23 Rules are stored as L<LaTeX::TikZ::Functor::Rule> objects.
24 They can apply not only to L<LaTeX::TikZ::Set> consumer objects, but also to the L<LaTeX::TikZ::Mod> consumer objects they contain.
25 When the functor is called against a set object and that the returned set is different from the original (as told by C<==>, which defaults to object identity), then the functor is also applied to all the mods of the set, and their transformed counterparts are added to the new set.
27 When the functor is called onto a set or mod object, all its associated rules are tried successively, and the handler of the first matching rule is executed with :
33 the functor object as its first argument ;
37 the current set or mod object as its second argument ;
41 the arguments passed to the functor itself starting at the third argument.
45 The handler is expected to return the new set or mod that will replace the old one in the resulting set tree.
47 If no matching rule is found, the object is returned as-is.
55 use LaTeX::TikZ::Functor::Rule;
57 use LaTeX::TikZ::Interface;
59 use LaTeX::TikZ::Tools;
61 my $lts_tc = LaTeX::TikZ::Tools::type_constraint('LaTeX::TikZ::Set');
65 $validate_spec = Sub::Name::subname('validate_spec' => sub {
68 my ($replace, $target);
69 if (defined $spec and ref $spec eq ''
70 and $spec =~ /^(\+?)([A-Za-z][A-Za-z0-9_]*(?:::[A-Za-z][A-Za-z0-9_]*)*)$/) {
71 $replace = defined($1) && $1 eq '+';
74 Carp::confess("Invalid rule spec $spec");
77 return $target, $replace;
85 my $functor = LaTeX::TikZ::Functor->new(
86 rules => [ $spec1 => $handler1, $spec2 => $handler2, ... ],
89 Creates a new functor object that will use both the default and the user-specified rules.
90 The functor is also a code reference that expects to be called against L<LaTeX::TikZ::Set> objects.
92 The default set and mod rules clone their relevant objects, so you get a clone functor (for the default set types) if you don't specify any user rule.
94 # The default is a clone method
95 my $clone = Tikz->functor;
96 my $dup = $set->$clone;
98 If there is already a default rule for one of the C<$spec>s, it is replaced by the new one ; otherwise, the user rule is inserted into the list of default rules after all its descendants' rules and before all its ancestors' rules.
101 my $translate = Tikz->functor(
102 # Only replace the way point sets are cloned
103 'LaTeX::TikZ::Set::Point' => sub {
104 my ($functor, $set, $x, $y) = @_;
111 label => $set->label,
116 my $shifted = $set->$translate(1, 1);
118 But if one of the C<$spec>s begins with C<'+'>, the rule will replace I<all> default rules that apply to subclasses or subroles of C<$spec> (including C<$spec> itself).
121 my $strip = Tikz->functor(
122 # Replace all existent mod rules by this simple one
123 '+LaTeX::TikZ::Mod' => sub { return },
125 my $naked = $set->$strip;
127 The functor will map unhandled sets and mods to themselves without cloning them, since it has no way to know how to do it.
128 Thus, if you define your own L<LaTeX::TikZ::Set> or L<LaTeX::TikZ::Mod> object, be sure to register a default rule for it with the L</default_rule> method.
132 my @default_set_rules;
133 my @default_mod_rules;
136 my ($class, %args) = @_;
138 my @set_rules = @default_set_rules;
139 my @mod_rules = @default_mod_rules;
141 my @user_rules = @{$args{rules} || []};
142 while (@user_rules) {
143 my ($spec, $handler) = splice @user_rules, 0, 2;
145 my ($target, $replace) = $validate_spec->($spec);
147 my $rule = LaTeX::TikZ::Functor::Rule->new(
153 into => $rule->is_set ? \@set_rules : \@mod_rules,
159 my %dispatch = map { $_->target => $_ } @set_rules, @mod_rules;
166 $lts_tc->assert_valid($set);
168 my $rule = $dispatch{ref($set)};
171 if ($_->handles($set)) {
177 return $set unless $rule;
179 my $new_set = $rule->handler->($self, $set, @_);
180 return $set if $new_set == $set;
184 for my $mod ($set->mods) {
185 my $rule = $dispatch{ref($mod)};
188 if ($_->handles($mod)) {
194 push @new_mods, $rule ? $rule->handler->($self, $mod, @_)
197 $new_set->mod(@new_mods);
203 LaTeX::TikZ::Interface->register(
207 __PACKAGE__->new(rules => \@_);
211 =head2 C<default_rule>
213 LaTeX::TikZ::Functor->default_rule($spec => $handler)
215 Adds to all subsequently created functors a default rule for the class or role C<$spec>.
217 An exception is thrown if there is already a default rule for C<$spec> ; otherwise, the new rule is inserted into the current list of default rules after all its descendants' rules and before all its ancestors' rules.
218 But if C<$spec> begins with C<'+'>, the rule will replace I<all> default rules that apply to subclasses or subroles of C<$spec> (including C<$spec> itself).
220 Returns true if and only if an existent rule was replaced.
226 my ($spec, $handler) = @_;
228 my ($target, $replace) = $validate_spec->($spec);
230 my $rule = LaTeX::TikZ::Functor::Rule->new(
236 into => $rule->is_set ? \@default_set_rules : \@default_mod_rules,
244 L<LaTeX::TikZ>, L<LaTeX::TikZ::Functor::Rule>.
248 Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
250 You can contact me by mail or on C<irc.perl.org> (vincent).
254 Please report any bugs or feature requests to C<bug-latex-tikz at rt.cpan.org>, or through the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=LaTeX-TikZ>.
255 I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
259 You can find documentation for this module with the perldoc command.
263 =head1 COPYRIGHT & LICENSE
265 Copyright 2010,2011,2012,2013,2014,2015 Vincent Pit, all rights reserved.
267 This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
271 1; # End of LaTeX::TikZ::Functor