File Coverage

blib/lib/Mojo/DOM/Role/Restrict.pm
Criterion Covered Total %
statement 107 116 92.2
branch 52 82 63.4
condition 31 36 86.1
subroutine 19 19 100.0
pod 5 7 71.4
total 214 260 82.3


line stmt bran cond sub pod time code
1             package Mojo::DOM::Role::Restrict;
2 10     10   6000409 use strict; use warnings; our $VERSION = 0.06;
  10     10   28  
  10         410  
  10         58  
  10         23  
  10         1177  
3 10     10   565 use Mojo::Base -role;
  10         8849  
  10         129  
4 10     10   8611 use Mojo::Util qw(xml_escape);
  10         152360  
  10         900  
5 10     10   78 use File::Spec;
  10         18  
  10         12495  
6              
7 32 100   32 0 15295 sub to_string { $_[1] ? ${$_[0]}->render : $_[0]->render; }
  14         84  
8              
9 18     18 0 93 sub render { _render($_[0]->tree, $_[0]->xml, $_[0]->restrict_spec) }
10              
11             around parse => sub {
12             my ($orig, $self) = (shift, shift);
13             $self->restrict_spec($_[1] || $self->restrict_spec || {
14             '*' => { '*' => 1 }
15             });
16             return $self->$orig(@_);
17             };
18              
19             sub restrict_spec {
20 79 100   79 1 925 if ( $_[1] ) {
21 36   100     89 $_[1]->{$_} && ! ref $_[1]->{$_} && do { $_[1]->{$_} = { '*' => 1 } } for (keys %{$_[1]});
  36   66     426  
  27         140  
22 36         86 ${$_[0]}->{restrict_spec} = $_[1];
  36         698  
23             }
24 79         141 ${$_[0]}->{restrict_spec};
  79         269  
25             }
26              
27 9     9 1 9822 sub valid { _valid($_[0]->tree, $_[0]->restrict_spec($_[1])) }
28              
29 12 50   12 1 12184 sub restrict { _restrict($_[0]->tree, $_[0]->restrict_spec($_[1])) && $_[0] }
30              
31             sub diff_module {
32 3 50 33 3 1 19 if ( $_[1] && $_[0]->diff_module_name !~ $_[1]) {
33 0         0 $_[0]->diff_module_name($_[1]);
34 0         0 $_[0]->diff_module_loaded(0);
35             }
36 3 50       11 $_[0]->diff_module_method($_[2]) if $_[2];
37 3 50       9 $_[0]->diff_module_params($_[3]) if defined $_[3];
38             return (
39 3         13 $_[0]->diff_module_name,
40             $_[0]->diff_module_method,
41             $_[0]->diff_module_params
42             );
43             }
44              
45             has diff_module_name => 'Text::Diff';
46              
47             has diff_module_loaded => 0;
48              
49             has diff_module_method => 'diff';
50              
51             has diff_module_params => sub { { style => 'Unified' } };
52              
53             sub diff {
54 3     3 1 3575 my ($self, $spec) = ($_[0], (shift)->restrict_spec(shift));
55 3         15 my ($module, $method, $params) = $self->diff_module(@_);
56 3 50       62 unless ( $self->diff_module_loaded ) {
57 3         164 my @parts = split /::|'/, $module, -1;
58 3 50 33     23 shift @parts if @parts && !$parts[0];
59 3         71 my $file = File::Spec->catfile( @parts );
60             LOAD_DIFF_MODULE: {
61 3         9 my $err;
  3         19  
62 3         10 for my $flag ( qw[1 0] ) {
63 3 50       14 my $load = $file . ($flag ? '.pm' : '');
64 3         7 eval { require $load };
  3         30  
65 3 50       14 $@ ? $err .= $@ : last LOAD_DIFF_MODULE;
66             }
67 0 0       0 die $err if $err;
68             }
69 3         13 $self->diff_module_loaded(1)
70             }
71             {
72 10     10   97 no strict 'refs';
  10         22  
  10         5700  
  3         100  
73 3         30 return *{"${module}::${method}"}->(\$self->to_string(1), \$self->to_string(), $params);
  3         37  
74             }
75             }
76              
77             # copy, paste and edit via Mojo::DOM::HTML::_render
78              
79             my %EMPTY = map { $_ => 1 } qw(area base br col embed hr img input keygen link menuitem meta param source track wbr);
80              
81             sub _render {
82 179     179   362 my ($tree, $xml, $spec) = @_;
83            
84             # Tag
85 179         366 my $type = $tree->[0];
86 179 100       400 if ($type eq 'tag') {
87              
88             # Start tag
89 108         199 my ($tag, $attrs) = _valid_tag($spec, $tree->[1], {%{$tree->[2]}});
  108         354  
90            
91 108 100       504 return '' unless $tag;
92            
93 92         165 my $result = "<$tag";
94              
95             # Attributes
96 92         149 for (sort keys %{$attrs}) {
  92         352  
97 90         553 my ($key, $value) = _valid_attribute($spec, $tag, $_, $attrs->{$_});
98 90 0       570 $result .= defined $value
    50          
    100          
99             ? qq{ $key="} . xml_escape($value) . '"'
100             : $xml
101             ? qq{ $key="$key"}
102             : " $key"
103             if $key;
104             }
105              
106             # No children
107 92 0       338 return $xml ? "$result />" : $EMPTY{$tag} ? "$result>" : "$result>" unless $tree->[4];
    0          
    50          
108              
109             # Children
110 10     10   82 no warnings 'recursion';
  10         24  
  10         15354  
111 92         237 $result .= '>' . join '', map { _render($_, $xml, $spec) } @$tree[4 .. $#$tree];
  143         696  
112              
113             # End tag
114 92         855 return "$result";
115             }
116              
117             # Text (escaped)
118 71 100       222 return xml_escape $tree->[1] if $type eq 'text';
119              
120             # Raw text
121 21 100       65 return $tree->[1] if $type eq 'raw';
122              
123             # Root
124 18 50       86 return join '', map { _render($_, $xml, $spec) } @$tree[1 .. $#$tree] if $type eq 'root';
  18         100  
125              
126             # DOCTYPE
127 0 0       0 return '[1] . '>' if $type eq 'doctype';
128              
129             # Comment
130 0 0       0 return '' if $type eq 'comment';
131              
132             # CDATA
133 0 0       0 return '[1] . ']]>' if $type eq 'cdata';
134              
135             # Processing instruction
136 0 0       0 return '[1] . '?>' if $type eq 'pi';
137              
138             # Everything else
139 0         0 return '';
140             }
141              
142             sub _valid_tag {
143 222     222   431 my ($spec, $tag, $attrs) = @_;
144 222   100     813 my $valid = $spec->{$tag} // $spec->{'*'};
145             return ref $valid && $valid->{validate_tag}
146 222 100 100     1238 ? $valid->{validate_tag}($tag, $attrs)
    100          
147             : $valid
148             ? ($tag, $attrs)
149             : 0;
150             }
151              
152             sub _valid_attribute {
153 166     166   381 my ($spec, $tag, $attr, $value) = @_;
154 166   100     1068 my $valid = $spec->{$tag}->{$attr} // $spec->{$tag}->{'*'} // $spec->{'*'}->{$attr} // $spec->{'*'}->{'*'};
      100        
      100        
155 166 100 100     1109 return ref $valid
    100          
156             ? $valid->($attr, $value)
157             : ($valid and $valid =~ m/1/ || $value =~ m/$valid/)
158             ? ( $attr, $value )
159             : 0;
160             }
161              
162             sub _valid {
163 58     58   167 my ($tree, $spec) = @_;
164 58 100       216 if ($tree->[0] eq 'tag') {
    100          
165 38         77 my ($tag, $attrs) = _valid_tag($spec, $tree->[1], {%{$tree->[2]}});
  38         129  
166 38 100       114 return 0 unless $tag;
167             _valid_attribute($spec, $tag, $_, $attrs->{$_}) or return 0
168 34   100     54 for (sort keys %{$attrs});
  34         102  
169 32 50       76 if ($tree->[4]) {
170 32   100     90 _valid($_, $spec) or return 0 for ( @$tree[4 .. $#$tree] );
171             }
172             } elsif ($tree->[0] eq 'root') {
173 9   100     46 _valid($_, $spec) or return 0 for ( @$tree[1 .. $#$tree] );
174             }
175 36         116 return 1;
176             }
177              
178             sub _restrict {
179 120     120   239 my ($tree, $spec) = @_;
180 120 100       293 if ($tree->[0] eq 'tag') {
    100          
181 76         162 my ($tag, $attrs) = _valid_tag($spec, $tree->[1], $tree->[2]);
182 76 100       262 return 0 unless $tag;
183 64         111 $tree->[1] = $tag;
184 64         88 for (sort keys %{$attrs}) {
  64         209  
185 64         154 my ($key, $value) = _valid_attribute($spec, $tag, $_, delete $attrs->{$_});
186 64 100       392 $attrs->{$key} = $value if $key;
187             }
188 64 50       173 if ($tree->[4]) {
189 64         95 my $i = 4;
190 12         74 _restrict($_, $spec) ? $i++ : splice(@{$tree}, $i, 1)
191 64 100       228 for ( @$tree[$i .. $#$tree] );
192             }
193             } elsif ($tree->[0] eq 'root') {
194 12         26 my $i = 1;
195 0         0 _restrict($_, $spec) ? $i++ : splice(@{$tree}, $i, 1)
196 12 50       84 for ( @$tree[$i .. $#$tree] );
197             }
198 108         277 return 1;
199             }
200              
201              
202             1;
203              
204             # TODO pretty print (for diff) and minmize.
205              
206             __END__