File Coverage

blib/lib/Mojolicious/Plugin/PPI.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::PPI;
2              
3 5     5   3765 use Mojo::Base 'Mojolicious::Plugin';
  5         10  
  5         35  
4              
5 5     5   992 use Mojo::Util;
  5         8  
  5         236  
6 5     5   33 use Mojo::ByteStream 'b';
  5         5  
  5         292  
7              
8 5     5   25 use File::Basename ();
  5         6  
  5         66  
9 5     5   31 use File::Spec;
  5         9  
  5         137  
10              
11 5     5   1117 use PPI::HTML;
  0            
  0            
12              
13             our $VERSION = '0.07';
14             $VERSION = eval $VERSION;
15              
16             has 'line_numbers' => 1;
17             has 'no_check_file' => 0;
18             has 'ppi_html_on' => sub { PPI::HTML->new( line_numbers => 1 ) };
19             has 'ppi_html_off' => sub { PPI::HTML->new( line_numbers => 0 ) };
20             has 'src_folder';
21              
22             has style => <<'END';
23             .ppi-code {
24             display: inline-block;
25             min-width: 400px;
26             background-color: #F8F8F8;
27             border-radius: 10px;
28             padding: 15px;
29             }
30             END
31              
32             has class_style => sub {{
33             line_number_on => { display => 'inline' },
34             line_number_off => { display => 'none' },
35              
36             cast => '#339999',
37             comment => '#008080',
38             core => '#FF0000',
39             double => '#999999',
40             heredoc_content => '#FF0000',
41             interpolate => '#999999',
42             keyword => '#BD2E2A',
43             line_number => '#666666',
44             literal => '#999999',
45             magic => '#0099FF',
46             match => '#9900FF',
47             number => '#990000',
48             operator => '#DD7700',
49             pod => '#008080',
50             pragma => '#A33AF7',
51             regex => '#9900FF',
52             single => '#FF33FF',
53             substitute => '#9900FF',
54             symbol => '#389A7D',
55             transliterate => '#9900FF',
56             word => '#999999',
57             }};
58              
59             sub register {
60             my ($plugin, $app) = (shift, shift);
61             $plugin->initialize($app, @_);
62              
63             push @{$app->static->classes}, __PACKAGE__;
64             push @{$app->renderer->classes}, __PACKAGE__;
65              
66             $app->helper( ppi => sub {
67             return $plugin if @_ == 1;
68             return $plugin->convert(@_);
69             });
70             $app->helper( ppi_css => sub { $_[0]->ppi->generate_css(@_) } );
71             }
72              
73             sub initialize {
74             my ($plugin, $app) = (shift, shift);
75             my %opts = @_ == 1 ? %{$_[0]} : @_;
76             my @unknown;
77             foreach my $key (keys %opts) {
78             my $code = $plugin->can($key);
79             unless ($code) {
80             push @unknown, $key;
81             next;
82             }
83             $plugin->$code($opts{$key});
84             }
85              
86             if ( @unknown ) {
87             warn "Unknown option(s): " . join(", ", @unknown) . "\n";
88             }
89             }
90              
91             sub convert {
92             my $plugin = shift;
93             my $c = shift;
94              
95             my %opts = $plugin->process_converter_opts(@_);
96              
97             my $converter =
98             $opts{line_numbers}
99             ? $plugin->ppi_html_on
100             : $plugin->ppi_html_off;
101              
102             my $id = $plugin->generate_id($c);
103              
104             my @tag = (
105             $opts{inline} ? 'code' : 'pre',
106             id => $id,
107             class => 'ppi-code ' . ($opts{inline} ? 'ppi-inline' : 'ppi-block'),
108             );
109              
110             if ($opts{line_numbers}) {
111             push @tag, ondblclick => "ppi_toggleLineNumbers('$id')";
112             $c->stash('ppi.js.required' => 1);
113             }
114              
115             my %render_opts = (
116             'ppi.code' => $converter->html( $opts{file} ? $opts{file} : \$opts{string} ),
117             'ppi.tag' => \@tag,
118             );
119              
120             #TODO use render_to_string once Mojo 5.00 is required
121             return $c->include('ppi_template', %render_opts);
122             }
123              
124             sub generate_id {
125             my ($plugin, $c) = @_;
126             return 'ppi' . $c->stash->{'ppi.id'}++;
127             }
128              
129             sub check_file {
130             my ($self, $file) = @_;
131             return undef if $self->no_check_file;
132              
133             if ( my $folder = $self->src_folder ) {
134             die "Could not find folder $folder\n" unless -d $folder;
135             $file = File::Spec->catfile( $folder, $file );
136             }
137              
138             return -e $file ? $file : undef;
139             }
140              
141             sub process_converter_opts {
142             my $plugin = shift;
143              
144             my $string = do {
145             no warnings 'uninitialized';
146             if (ref $_[-1] eq 'CODE') {
147             Mojo::Util::trim pop->();
148             }
149             };
150              
151             my %opts;
152             if (ref $_[-1]) {
153             %opts = %{ pop() };
154             }
155              
156             if ( @_ % 2 ) {
157             die "Cannot specify both a string and a block\n" if $string;
158              
159             $string = shift;
160             $opts{file} = $plugin->check_file($string);
161             unless ( $opts{file} ) {
162             $opts{inline} //= 1;
163             }
164              
165             }
166              
167             %opts = (%opts, @_) if @_;
168              
169             $opts{string} = $string unless defined $opts{file};
170              
171             $opts{line_numbers} //= 0 if $opts{inline};
172             $opts{line_numbers} //= $plugin->line_numbers;
173              
174             return %opts;
175             }
176              
177             sub generate_css {
178             my ($plugin, $c) = @_;
179             my $sheet = b("pre.ppi-code br { display: none; }\n");
180             $$sheet .= $plugin->style."\n";
181             my $cs = $plugin->class_style;
182             foreach my $key (sort keys %$cs) {
183             my $value = $cs->{$key};
184             $value = { color => $value } unless ref $value;
185             $$sheet .= ".ppi-code .$key { ";
186             foreach my $prop ( sort keys %$value ) {
187             $$sheet .= "$prop: $value->{$prop}; ";
188             }
189             $$sheet .= "}\n";
190             }
191             return $c->stylesheet(sub{$sheet});
192             }
193              
194             1;
195              
196             __DATA__