File Coverage

blib/lib/WWW/Shopify/Liquid/Debugger.pm
Criterion Covered Total %
statement 72 107 67.2
branch 9 32 28.1
condition 3 12 25.0
subroutine 19 24 79.1
pod 0 13 0.0
total 103 188 54.7


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3 37     37   85677 use strict;
  37         111  
  37         1229  
4 37     37   232 use warnings;
  37         95  
  37         1167  
5              
6 37     37   443 use WWW::Shopify::Liquid;
  37         91  
  37         6247  
7              
8             package WWW::Shopify::Liquid::Debugger::Breakpoint;
9              
10             sub new {
11 1     1   4 my ($package) = shift;
12 1         4 my $hash = shift;
13 1 50       5 $hash = {} unless $hash;
14 1         9 return bless {
15             %$hash
16             }, $package;
17             }
18              
19 5     5   43 sub line { return $_[0]->{line}; }
20 5     5   46 sub file { return $_[0]->{file}; }
21              
22              
23             # Provides a low-level debugging interface to liquid.
24              
25             package WWW::Shopify::Liquid::Debugger;
26              
27 86     86 0 1183 sub new { return bless {
28             renderer => $_[1],
29             breakpoints => [],
30            
31             break_line => undef,
32             current_line => undef
33             }, $_[0]; }
34              
35 37     37   326 use Scalar::Util qw(blessed reftype weaken);
  37         121  
  37         2555  
36 37     37   288 use List::MoreUtils qw(uniq);
  37         96  
  37         576  
37              
38             my @active_debuggers = ();
39             my $hooked_packages = {};
40              
41             sub dump {
42 0     0 0 0 my ($self, $item, $level) = @_;
43 0 0       0 $level = 0 unless $level;
44 0         0 my $tab = "\t";
45 0         0 my $tabs = join("", ($tab x $level));
46 0         0 my $accumulator = '';
47 0 0       0 if (reftype($item)) {
48 0 0       0 if (reftype($item) eq 'HASH') {
    0          
49 0 0       0 if (blessed($item)) {
50 0         0 $accumulator .= do { my $a = ref($item); $a =~ s/WWW::Shopify::Liquid::(Tag|Token|Operator):://; uc($a) } . "\n";
  0         0  
  0         0  
  0         0  
51             } else {
52 0         0 $accumulator .= $tabs . ref($item) . "\n";
53             }
54            
55 0         0 my @keys = grep { $_ ne "line" } keys(%$item);
  0         0  
56 0 0       0 if (int(@keys) > 1) {
    0          
57 0         0 for (sort(@keys)) {
58 0         0 $accumulator.= "$tabs$tab" . $self->dump($item->{$_}, $level+1);
59             }
60             } elsif (int(@keys) > 0) {
61 0         0 $accumulator.= "$tabs$tab" . $self->dump($item->{$keys[0]}, $level+1);
62             }
63            
64             } elsif (reftype($item) eq 'ARRAY') {
65 0         0 $accumulator .= "\n";
66 0         0 $accumulator .= "$tabs$tab" . $self->dump($_, $level+1) for (grep { defined $_ } @$item);
  0         0  
67 0         0 $accumulator .= "$tabs\n";
68             }
69             } else {
70 0 0       0 return "null" unless defined $item;
71 0         0 return "'" . $item . "'\n";
72             }
73 0         0 return $accumulator;
74             }
75              
76             sub add_breakpoint {
77 1     1 0 12 my ($self, $context, $line) = @_;
78 1         3 push(@{$self->{breakpoints}}, WWW::Shopify::Liquid::Debugger::Breakpoint->new({ line => $line, file => $context }));
  1         17  
79             }
80              
81             sub remove_breakpoint {
82 1     1 0 2109 my ($self, $context, $line) = @_;
83 1 50       4 $self->{breakpoints} = [grep { $_->line != $line || $_->file ne $context } @{$self->{breakpoints}}];
  1         6  
  1         6  
84            
85             }
86              
87              
88             sub render {
89 3     3 0 2373 my ($self, $hash, $ast) = @_;
90 3         8 my $debugger = $self;
91             # Only breakpoint on tags.
92 3         19 my @tags = grep { $_->isa('WWW::Shopify::Liquid::Tag') } $ast->tokens;
  36         171  
93             # For every tag in here, modify the in-memory package, such that when render is called, before we call render, we call our new breakpoint method.
94 3         13 for (uniq(grep { !$hooked_packages->{$_} } map { ref($_) } @tags)) {
  9         43  
  9         30  
95 37     37   51452 no strict 'refs';
  37         144  
  37         1517  
96 37     37   246 no warnings 'redefine';
  37         108  
  37         1953  
97 37     37   247 no warnings 'closure';
  37         99  
  37         17433  
98            
99 2         8 my $qualified = $_ . "::render";
100 2         6 my $original = *{$qualified}{CODE};
  2         14  
101 2         8 $hooked_packages->{$_} = 1;
102 2 100       7 if ($original) {
103 1     3 0 129 eval ("package $_;
  3         26  
  3         20  
104             sub render {
105             WWW::Shopify::Liquid::Debugger->package_step(\$_[0]);
106             \$original->(\@_);
107             }");
108 1 50       10 if (my $exp = $@) {
109 0         0 die $exp;
110             }
111             } else {
112 1     3 0 95 eval ("package $_;
  3         23  
  3         26  
113             sub render {
114             WWW::Shopify::Liquid::Debugger->package_step(\$_[0]);
115             shift->SUPER::render(\@_);
116             }");
117             }
118             }
119 3         15 push(@active_debuggers, $self);
120 3         22 my @results = $self->{renderer}->render($hash, $ast);
121 3         9 @active_debuggers = grep { $_ != $self } @active_debuggers;
  3         17  
122 3 50       12 return @results if wantarray;
123 3         20 return $results[0];
124            
125             }
126              
127             sub package_step {
128 6     6 0 22 my ($self, $element) = @_;
129 6         36 $_->step($element) for (@active_debuggers);
130             }
131              
132              
133             sub step {
134 6     6 0 16 my ($self, $element) = @_;
135 6         27 $self->{current_line} = $element->{line};
136 6 50 33     13 if (int(grep { $element->{line} && $element->{line}->[3] && $_->file eq $element->{line}->[3] && $element->{line}->[0] == $_->line } @{$self->{breakpoints}}) > 0) {
  4 100 33     36  
  6         24  
137 2         11 $self->break($element);
138             }
139 6 0 33     249 if ($self->{break_line} && $element->{line} && $element->{line} >= $self->{break_line}) {
      0        
140 0         0 $self->{break_line} = undef;
141 0         0 $self->break($element);
142             }
143             }
144              
145             sub continue {
146 0     0 0 0 my ($self) = @_;
147             }
148              
149              
150             sub next_line {
151 0     0 0 0 my ($self) = @_;
152 0         0 $self->{break_line} = $self->{current_line}+1;
153 0         0 $self->continue;
154             }
155              
156              
157             sub break {
158 0     0 0 0 my ($self) = @_;
159             }
160              
161             sub variable {
162 0     0 0 0 my ($self) = @_;
163            
164             }
165              
166              
167             1;