File Coverage

blib/lib/LaTeX/TikZ/Functor/Rule.pm
Criterion Covered Total %
statement 60 61 98.3
branch 21 26 80.7
condition 2 3 66.6
subroutine 8 8 100.0
pod 2 2 100.0
total 93 100 93.0


line stmt bran cond sub pod time code
1             package LaTeX::TikZ::Functor::Rule;
2              
3 10     10   33 use strict;
  10         366  
  10         267  
4 10     10   37 use warnings;
  10         438  
  10         290  
5              
6             =head1 NAME
7              
8             LaTeX::TikZ::Functor::Rule - An object that specifies how functors should handle a certain kind of set or mod.
9              
10             =head1 VERSION
11              
12             Version 0.03
13              
14             =cut
15              
16             our $VERSION = '0.03';
17              
18             =head1 DESCRIPTION
19              
20             A rule specifies how functors (L objects) should handle a certain kind of set or mod.
21             A functor is basically an ordered collection of rules.
22              
23             =cut
24              
25 10     10   32 use Carp ();
  10         324  
  10         96  
26              
27 10     10   28 use Mouse;
  10         7  
  10         35  
28 10     10   2269 use Mouse::Util qw;
  10         13  
  10         39  
29 10     10   668 use Mouse::Util::TypeConstraints;
  10         12  
  10         38  
30              
31             =head1 ATTRIBUTES
32              
33             =head2 C
34              
35             A class or role name against which set or mod candidates will be matched.
36             It must consume either L or L, directly or through inheritance.
37              
38             =cut
39              
40             has 'target' => (
41             is => 'ro',
42             isa => 'ClassName|RoleName',
43             required => 1,
44             );
45              
46             =head2 C
47              
48             The code reference executed when the rule handles a given set or mod object.
49             It is called with the L object as its first argument, the set/mod object as its second, and then the arguments passed to the functor itself.
50              
51             =cut
52              
53             has 'handler' => (
54             is => 'ro',
55             isa => 'CodeRef',
56             required => 1,
57             );
58              
59             =head2 C
60              
61             True if and only if the target is a role.
62              
63             =cut
64              
65             has 'is_role' => (
66             is => 'ro',
67             isa => 'Bool',
68             required => 1,
69             );
70              
71             =head2 C
72              
73             True when the target does the L role, and false when it does L.
74              
75             =cut
76              
77             has 'is_set' => (
78             is => 'ro',
79             isa => 'Bool',
80             required => 1,
81             );
82              
83             my $ltfrl_tc = subtype 'LaTeX::TikZ::Functor::RuleList'
84             => as 'ArrayRef[LaTeX::TikZ::Functor::Rule]';
85              
86             =head1 METHODS
87              
88             =head2 C
89              
90             my $rule = LaTeX::TikZ::Functor::Rule->new(
91             target => $target,
92             handler => $handler,
93             );
94              
95             Constructs a new rule object with target C<$target> and handler C<$handler>.
96              
97             =cut
98              
99             around 'BUILDARGS' => sub {
100             my ($orig, $class, %args) = @_;
101              
102             my $target = $args{target};
103             __PACKAGE__->meta->find_attribute_by_name('target')
104             ->type_constraint->assert_valid($target);
105              
106             (my $pm = $target) =~ s{::}{/}g;
107             $pm .= '.pm';
108             require $pm;
109              
110             my $meta = find_meta($target);
111             Carp::confess("No meta object associated with target $target")
112             unless defined $meta;
113             $args{is_role} = $meta->isa('Mouse::Meta::Role');
114              
115             my $is_set;
116             if (does_role($target, 'LaTeX::TikZ::Set')) {
117             $is_set = 1;
118             } elsif (does_role($target, 'LaTeX::TikZ::Mod')) {
119             $is_set = 0;
120             } else {
121             Carp::confess("Target $target is neither a set nor a mod");
122             }
123             $args{is_set} = $is_set;
124              
125             $class->$orig(%args);
126             };
127              
128             =head2 C
129              
130             my $has_replaced = $rule->insert(
131             into => \@list,
132             overwrite => $overwrite,
133             replace => $replace,
134             );
135              
136             Inserts the current rule into the list of rules C<@list>.
137             The list is expected to be ordered, in that each rule must come after all the rules that have a target that inherits or consumes the original rule's own target.
138              
139             If C<$replace> is false, then the rule will be inserted into C<@list> after all the rules applying to the target's subclasses/subroles and before all its superclasses/superroles ; except if there is already an existent entry for the same target, in which case it will be overwritten if C<$overwrite> is true, or an exception will be thrown if it is false.
140              
141             If C<$replace> is true, then the rule will replace the first rule in the list that is a subclass or that consumes the role denoted by the target.
142             All the subsequent rules in the list that inherit or consume the target will be removed.
143              
144             Returns true if and only if an existent rule was replaced.
145              
146             =cut
147              
148             sub insert {
149 116     116 1 275 my ($rule, %args) = @_;
150              
151 116         139 my $list = $args{into};
152 116         249 $ltfrl_tc->assert_valid($list);
153              
154 116         763 my $overwrite = $args{overwrite};
155 116         143 my $replace = $args{replace};
156              
157 116 100       179 if ($replace) {
158 5         3 my (@remove, $replaced);
159              
160 5         11 for my $i (0 .. $#$list) {
161 21         38 my $old_target = $list->[$i]->target;
162 21 50       24 if ($rule->handles($old_target)) {
163 21 100       743 if ($replaced) {
164 16         27 push @remove, $i;
165             } else {
166 5         10 splice @$list, $i, 1, $rule;
167 5         11 $replaced = 1;
168             }
169             }
170             }
171              
172 5         7 my $shift = 0;
173 5         8 for (@remove) {
174 16         13 splice @$list, $_ - $shift, 1;
175 16         15 ++$shift;
176             }
177 5 50       27 return 1 if $replaced;
178              
179             } else { # Replace only an existent rule
180 111         212 my $target = $rule->target;
181              
182 111         107 my $last_descendant = undef;
183 111         105 my $first_ancestor = undef;
184              
185 111         241 for my $i (0 .. $#$list) {
186 399         345 my $old_rule = $list->[$i];
187 399         547 my $old_target = $old_rule->target;
188 399 100       648 if ($old_target eq $target) {
    100          
    100          
189 9 50       32 Carp::confess("Default rule already defined for target $target")
190             unless $overwrite;
191 9         26 splice @$list, $i, 1, $rule;
192 9         50 return 1;
193             } elsif ($rule->handles($old_target)) {
194 1         31 $last_descendant = $i;
195             } elsif ($old_rule->handles($target)) {
196 12         86 $first_ancestor = $i;
197             }
198             }
199              
200 102         105 my $pos;
201 102 100       237 if (defined $first_ancestor) {
    50          
202 12 50 66     44 Carp::confess("Unsorted rule list")
203             if defined $last_descendant and $first_ancestor <= $last_descendant;
204 12         16 $pos = $first_ancestor;
205             } elsif (defined $last_descendant) {
206 0         0 $pos = $last_descendant + 1;
207             }
208              
209 102 100       183 if (defined $pos) {
210 12         27 splice @$list, $pos, 0, $rule;
211 12         44 return 0;
212             }
213             }
214              
215 90         131 push @$list, $rule;
216 90         251 return 0;
217             }
218              
219             =head2 C
220              
221             $rule->handles($obj);
222              
223             Returns true if and only if the current rule can handle the object or class/role name C<$obj>.
224              
225             =cut
226              
227             sub handles {
228 810     810 1 663 my ($rule, $obj) = @_;
229              
230 810 100       4796 $rule->is_role ? does_role($obj, $rule->target) : $obj->isa($rule->target);
231             }
232              
233             __PACKAGE__->meta->make_immutable;
234              
235             =head1 SEE ALSO
236              
237             L, L.
238              
239             =head1 AUTHOR
240              
241             Vincent Pit, C<< >>, L.
242              
243             You can contact me by mail or on C (vincent).
244              
245             =head1 BUGS
246              
247             Please report any bugs or feature requests to C, or through the web interface at L.
248             I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
249              
250             =head1 SUPPORT
251              
252             You can find documentation for this module with the perldoc command.
253              
254             perldoc LaTeX::TikZ
255              
256             =head1 COPYRIGHT & LICENSE
257              
258             Copyright 2010,2011,2012,2013,2014,2015 Vincent Pit, all rights reserved.
259              
260             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
261              
262             =cut
263              
264             1; # End of LaTeX::TikZ::Functor::Rule