File Coverage

blib/lib/HTML/Builder.pm
Criterion Covered Total %
statement 79 89 88.7
branch 3 4 75.0
condition 2 2 100.0
subroutine 25 30 83.3
pod 11 12 91.6
total 120 137 87.5


line stmt bran cond sub pod time code
1             #
2             # This file is part of HTML-Builder
3             #
4             # This software is Copyright (c) 2012 by Chris Weyl.
5             #
6             # This is free software, licensed under:
7             #
8             # The GNU Lesser General Public License, Version 2.1, February 1999
9             #
10             package HTML::Builder;
11             {
12             $HTML::Builder::VERSION = '0.008';
13             }
14              
15             # ABSTRACT: A declarative approach to HTML generation
16              
17 5     5   209989 use v5.10;
  5         22  
  5         246  
18              
19 5     5   29 use strict;
  5         11  
  5         170  
20 5     5   28 use warnings;
  5         7  
  5         557  
21              
22 5     5   7144 use Capture::Tiny 0.15 'capture_stdout';
  5         285236  
  5         833  
23 5     5   6417 use HTML::Tiny;
  5         24387  
  5         276  
24 5     5   5504 use Sub::Install;
  5         10463  
  5         37  
25 5     5   4309 use List::MoreUtils 'uniq';
  5         7737  
  5         4225  
26              
27             # debugging...
28             #use Smart::Comments;
29              
30             our $IN_TAG;
31              
32              
33             # HTML5 tags from:
34             # http://en.wikipedia.org/wiki/HTML5#Differences_from_HTML.C2.A04.01_and_XHTML.C2.A01.x
35             # 18 Feb 2012
36             #
37             # Other HTML tags from:
38             # http://www.w3schools.com/tags/default.asp
39             # 19 Feb 2012
40              
41              
42             # !--...-- Defines
43              
44 11     11 1 140 sub html5_tags { qw{
45              
46             article aside audio bdo canvas command datalist details embed figcaption
47             figure footer header hgroup keygen mark meter nav output progress rp rt
48             ruby section source summary time video wbr
49              
50             } }
51              
52              
53 0     0 1 0 sub html5_minimal_tags { q{ article aside footer header nav } }
54              
55             # excl: s
56 13     13 1 1557 sub deprecated_tags { qw{ applet basefont center dir font menu strike u xmp } }
57 0     0 0 0 sub depreciated_tags { deprecated_tags() }
58              
59              
60             sub conflicting_tags { {
61 16     16 1 3331 html_sub => 'sub',
62             html_map => 'map',
63             html_q => 'q',
64             html_tr => 'tr',
65             } }
66              
67             sub html_tags {
68              
69             # excl: sub map q tr
70              
71 11     11 1 187 return qw{
72              
73             a abbr acronym address area b
74             base bdo big blockquote body br
75             button caption cite code col colgroup
76             dd del dfn div dl dt
77             em fieldset form frame frameset h1
78             head hr html i iframe img
79             input ins kbd label legend li
80             link meta noframes noscript object
81             ol optgroup option p param pre
82             samp script select small span
83             strong style sup table
84             tbody td textarea tfoot th thead
85             title tt ul var
86              
87             };
88             }
89              
90              
91 0     0 1 0 sub form_tags { qw{
92              
93             form fieldset button input label optgroup option select textarea
94              
95             }}
96              
97              
98 0     0 1 0 sub table_tags { qw{ table tr td th thead tbody tfoot } }
99              
100              
101             sub minimal_tags {
102 2     2 1 27 return ('h1'..'h5', qw{
103             div span p img script br ul ol li style a
104             });
105             }
106              
107              
108             sub our_tags {
109 11         3575 my @tags = (
110             html5_tags(),
111             html_tags(),
112             deprecated_tags(),
113 11     11 1 115 (keys %{ conflicting_tags() }),
114             );
115 11         1721 return uniq sort @tags;
116             }
117              
118              
119 0     0   0 sub _attr { die }
120              
121             sub attr(&) {
122 3     3 1 24 my $code = shift;
123              
124             ### in attr...
125 3         51 _attr($code->());
126             }
127              
128             sub _is_autoload_gen {
129 534     534   806 my ($attr_href) = @_;
130              
131             return sub {
132 261     261   2730 shift;
133              
134 261         515 my $field = our $AUTOLOAD;
135 261         1622 $field =~ s/.*:://;
136              
137             # XXX
138 261         569 $field =~ s/__/:/g; # xml__lang is 'foo' ====> xml:lang="foo"
139 261         385 $field =~ s/_/-/g; # http_equiv is 'bar' ====> http-equiv="bar"
140              
141             # Squash empty values, but not '0' values
142 261 50       476 my $val = join ' ', grep { defined $_ && $_ ne '' } @_;
  261         1847  
143              
144             #push @$attr_aref, $field => $val;
145 261         561 $attr_href->{$field} = $val;
146              
147 261         2692 return;
148 534         2722 };
149             }
150              
151             sub _attr_gen {
152 534     534   721 my ($attr_href) = @_;
153              
154             return sub {
155              
156 3     3   19 my %attrs = @_;
157             ### %attrs
158 3         11 @{$attr_href}{keys %attrs} = (values %attrs);
  3         7  
159              
160 3         36 return;
161 534         2622 };
162             }
163              
164             sub tag($&) {
165 534     534 1 1089 my ($tag, $inner_coderef) = @_;
166              
167 534         744 state $h = HTML::Tiny->new;
168              
169             ### @_
170 534         1432 my %attrs = ();
171              
172             # This is almost completely stolen from Template::Declare::Tags, and
173             # completely terrifying in that it confirms my dark suspicions on how
174             # it was achieved over there.
175 5     5   48 no warnings 'once', 'redefine';
  5         13  
  5         5905  
176 534         1488 local *gets::AUTOLOAD = _is_autoload_gen(\%attrs);
177 534         1414 local *_attr = _attr_gen(\%attrs);
178 534         1011 my $inner = q{};
179 534   100 534   15292 my $stdout = capture_stdout { local $IN_TAG = 1; $inner .= $inner_coderef->() || q{} };
  534         414507  
  534         9471  
180              
181 534         464649 my $return = $h->tag($tag, \%attrs, "$stdout$inner");
182              
183             ### $return
184 534 100       36648 if ($IN_TAG) {
185 4         190 print $return;
186 4         54 return q{};
187             }
188             else {
189 530         11548 return $return;
190             }
191             }
192              
193             use Sub::Exporter -setup => {
194              
195             exports => [ our_tags, 'attr', 'tag' ],
196             groups => {
197              
198             default => ':minimal',
199              
200 1         143 minimal => sub { _generate_group([ minimal_tags ], @_) },
201 0         0 html5 => sub { _generate_group([ html5_tags ], @_) },
202 0         0 html5_minimal => sub { _generate_group([ html5_minimal_tags ], @_) },
203 1         236 deprecated => sub { _generate_group([ deprecated_tags ], @_) },
204 1         270 depreciated => sub { _generate_group([ deprecated_tags ], @_) },
205 0         0 table => sub { _generate_group([ table_tags ], @_) },
206 0         0 form => sub { _generate_group([ form_tags ], @_) },
207 0         0 header => sub { _generate_group([qw{ header hgroup } ], @_) },
208             },
209 5     5   6270 };
  5         77293  
  5         32  
210              
211             sub _generate_group {
212 3     3   8 my ($tags, $class, $group, $arg) = @_;
213              
214             return {
215 34         40 attr => \&attr,
216 3     72   1248 map { my $tag = $_; $tag => sub(&) { unshift @_, $tag; goto \&tag } }
  34         141  
  72         26894  
  72         271  
217             @$tags
218             };
219             }
220              
221             {
222             my $_install = sub {
223             my ($subname, $tag) = @_;
224             $tag ||= $subname;
225             Sub::Install::install_sub({
226 462     462   323950 code => sub(&) { unshift @_, $tag; goto \&tag },
  462         1713  
227             as => $tag,
228             });
229             };
230              
231             my $conflict = conflicting_tags;
232             $_install->($_) for our_tags;
233             $_install->(@$_) for map { [ $_ => $conflict->{$_} ] } keys %$conflict;
234             }
235              
236             !!42;
237              
238             __END__