File Coverage

blib/lib/Gnuplot/Builder/Script.pm
Criterion Covered Total %
statement 228 238 95.8
branch 70 86 81.4
condition 12 18 66.6
subroutine 53 54 98.1
pod 26 27 96.3
total 389 423 91.9


line stmt bran cond sub pod time code
1             package Gnuplot::Builder::Script;
2 37     37   2971372 use strict;
  37         61  
  37         1197  
3 37     37   141 use warnings;
  37         68  
  37         1697  
4 37     37   14375 use Gnuplot::Builder::PrototypedData;
  37         115  
  37         1200  
5 37     37   211 use Gnuplot::Builder::Util qw(quote_gnuplot_str);
  37         53  
  37         1640  
6 37     37   15100 use Gnuplot::Builder::Process;
  37         104  
  37         1406  
7 37     37   187 use Scalar::Util qw(weaken blessed refaddr);
  37         49  
  37         2236  
8 37     37   172 use Carp;
  37         49  
  37         1729  
9 37     37   19783 use overload '""' => "to_string";
  37         52931  
  37         198  
10              
11             sub new {
12 1177     1177 1 4212148 my ($class, @set_args) = @_;
13 1177         2355 my $self = bless {
14             pdata => undef,
15             parent => undef,
16             };
17 1177         2010 $self->_init_pdata();
18 1177 100       1742 if(@set_args) {
19 19         106 $self->set(@set_args);
20             }
21 1177         1968 return $self;
22             }
23              
24             sub _init_pdata {
25 1177     1177   1408 my ($self) = @_;
26 1177         1544 weaken $self;
27             $self->{pdata} = Gnuplot::Builder::PrototypedData->new(
28             entry_evaluator => sub {
29 41     41   62 my ($key, $value_code) = @_;
30 41 100       81 if(defined($key)) {
31 38         106 return $value_code->($self, substr($key, 1));
32             }else {
33 3         6 return $value_code->($self);
34             }
35             }
36 1177         4739 );
37             }
38              
39             sub add {
40 15     15 1 54 my ($self, @sentences) = @_;
41 15         29 foreach my $sentence (@sentences) {
42 20         51 $self->{pdata}->add_entry($sentence);
43             }
44 15         32 return $self;
45             }
46              
47             sub _set_entry {
48 113     113   313 my ($self, $prefix, $quote, @pairs) = @_;
49             $self->{pdata}->set_entry(
50 113         472 entries => \@pairs,
51             key_prefix => $prefix,
52             quote => $quote,
53             );
54 113         732 return $self;
55             }
56              
57             sub set {
58 77     77 1 939 my ($self, @pairs) = @_;
59 77         235 return $self->_set_entry("o", 0, @pairs);
60             }
61              
62             *set_option = *set;
63              
64             sub setq {
65 21     21 1 98 my ($self, @pairs) = @_;
66 21         42 return $self->_set_entry("o", 1, @pairs);
67             }
68              
69             *setq_option = *setq;
70              
71             sub unset {
72 2     2 1 10 my ($self, @names) = @_;
73 2         5 return $self->set(map { $_ => undef } @names);
  4         10  
74             }
75              
76             sub _get_entry {
77 75     75   117 my ($self, $prefix, $name) = @_;
78 75 50       191 croak "name cannot be undef" if not defined $name;
79 75         279 return $self->{pdata}->get_resolved_entry("$prefix$name");
80             }
81              
82             sub get_option {
83 62     62 1 138 my ($self, $name) = @_;
84 62         116 return $self->_get_entry("o", $name);
85             }
86              
87             sub _delete_entry {
88 9     9   23 my ($self, $prefix, @names) = @_;
89 9         19 foreach my $name (@names) {
90 11 50       28 croak "name cannot be undef" if not defined $name;
91 11         49 $self->{pdata}->delete_entry("$prefix$name");
92             }
93 9         31 return $self;
94             }
95              
96             sub delete_option {
97 5     5 1 17 my ($self, @names) = @_;
98 5         13 return $self->_delete_entry("o", @names);
99             }
100              
101             sub _create_statement {
102 262     262   381 my ($raw_key, $value) = @_;
103 262 100       431 return $value if !defined $raw_key;
104 217         534 my ($prefix, $name) = (substr($raw_key, 0, 1), substr($raw_key, 1));
105 217         284 my @words = ();
106 217 100       438 if($prefix eq "o") {
    50          
107 181 100       484 @words = defined($value) ? ("set", $name, $value) : ("unset", $name);
108             }elsif($prefix eq "d") {
109 36 100       86 @words = defined($value) ? ($name, "=", $value) : ("undefine", $name);
110             }else {
111 0         0 confess "Unknown key prefix: $prefix";
112             }
113 217         353 return join(" ", grep { "$_" ne "" } @words);
  623         1269  
114             }
115              
116             sub to_string {
117 234     234 1 2379 my ($self) = @_;
118 234         368 my $result = "";
119             $self->{pdata}->each_resolved_entry(sub {
120 250     250   7479 my ($raw_key, $values) = @_;
121 250         417 foreach my $value (@$values) {
122 262         449 my $statement = _create_statement($raw_key, $value);
123 262         413 $result .= $statement;
124 262 100       1211 $result .= "\n" if $statement !~ /\n$/;
125             }
126 234         1420 });
127 234         1880 return $result;
128             }
129              
130             sub define {
131 15     15 1 75 my ($self, @pairs) = @_;
132 15         39 return $self->_set_entry("d", 0, @pairs);
133             }
134              
135             *set_definition = *define;
136              
137             sub undefine {
138 1     1 1 9 my ($self, @names) = @_;
139 1         3 return $self->define(map { $_ => undef } @names);
  3         10  
140             }
141              
142             sub get_definition {
143 13     13 1 43 my ($self, $name) = @_;
144 13         41 return $self->_get_entry("d", $name);
145             }
146              
147             sub delete_definition {
148 4     4 1 15 my ($self, @names) = @_;
149 4         16 return $self->_delete_entry("d", @names);
150             }
151              
152             sub set_parent {
153 1012     1012 1 1261 my ($self, $parent) = @_;
154 1012 100       1313 if(!defined($parent)) {
155 1         2 $self->{parent} = undef;
156 1         4 $self->{pdata}->set_parent(undef);
157 1         4 return $self;
158             }
159 1011 50 33     2427 if(!blessed($parent) || !$parent->isa("Gnuplot::Builder::Script")) {
160 0         0 croak "parent must be a Gnuplot::Builder::Script"
161             }
162 1011         1125 $self->{parent} = $parent;
163 1011         1901 $self->{pdata}->set_parent($parent->{pdata});
164 1011         1401 return $self;
165             }
166              
167 5     5 1 50 sub get_parent { return $_[0]->{parent} }
168              
169             *parent = *get_parent;
170              
171             sub new_child {
172 1009     1009 1 2302 my ($self) = @_;
173 1009         1318 return Gnuplot::Builder::Script->new->set_parent($self);
174             }
175              
176             sub _collect_dataset_params {
177 76     76   184 my ($dataset_arrayref) = @_;
178 76         118 my @params_str = ();
179 76         101 my @dataset_objects = ();
180 76         120 foreach my $dataset (@$dataset_arrayref) {
181 91         152 my $ref = ref($dataset);
182 91 100       174 if(!$ref) {
183 72         143 push(@params_str, $dataset);
184             }else {
185 19 50 33     160 if(!$dataset->can("params_string") || !$dataset->can("write_data_to")) {
186 0         0 croak "You cannot use $ref object as a dataset.";
187             }
188 19         53 my ($param_str) = $dataset->params_string();
189 19         2786 push(@params_str, $param_str);
190 19         45 push(@dataset_objects, $dataset);
191             }
192             }
193 76         197 return (\@params_str, \@dataset_objects);
194             }
195              
196             sub _wrap_writer_to_detect_empty_data {
197 106     106   185 my ($writer) = @_;
198 106         141 my $ended_with_newline = 0;
199 106         129 my $data_written = 0;
200             my $wrapped_writer = sub {
201 38 100   38   247 my @nonempty_data = grep { defined($_) && $_ ne "" } @_;
  36         142  
202 38 100       83 return if !@nonempty_data;
203 31         41 $data_written = 1;
204 31         98 $ended_with_newline = ($nonempty_data[-1] =~ /\n$/);
205 31         80 $writer->(join("", @nonempty_data));
206 106         351 };
207 106         282 return ($wrapped_writer, \$data_written, \$ended_with_newline);
208             }
209              
210             sub _write_inline_data {
211 76     76   121 my ($writer, $dataset_objects_arrayref) = @_;
212 76         182 my ($wrapped_writer, $data_written_ref, $ended_with_newline_ref) =
213             _wrap_writer_to_detect_empty_data($writer);
214 76         399 foreach my $dataset (@$dataset_objects_arrayref) {
215 19         35 $$data_written_ref = $$ended_with_newline_ref = 0;
216 19         55 $dataset->write_data_to($wrapped_writer);
217 19 100       115 next if !$$data_written_ref;
218 11 100       29 $writer->("\n") if !$$ended_with_newline_ref;
219 11         32 $writer->("e\n");
220             }
221             }
222              
223             sub _wrap_commands_with_output {
224 159     159   294 my ($commands_ref, $output_filename) = @_;
225 159 100       289 if(defined($output_filename)) {
226 12         58 unshift @$commands_ref, "set output " . quote_gnuplot_str($output_filename);
227 12         35 push @$commands_ref, "set output";
228             }
229             }
230              
231             sub _draw_with {
232 76     76   251 my ($self, %args) = @_;
233 76         182 my $plot_command = $args{command};
234 76         113 my $dataset = $args{dataset};
235 76 50       182 croak "dataset parameter is mandatory" if not defined $dataset;
236 76 100       199 if(ref($dataset) ne "ARRAY") {
237 19         43 $dataset = [$dataset];
238             }
239 76 50       173 croak "at least one dataset is required" if !@$dataset;
240              
241             my $plotter = sub {
242 76     76   144 my $writer = shift;
243 76         165 my ($params, $dataset_objects) = _collect_dataset_params($dataset);
244 76         372 $writer->("$plot_command " . join(",", @$params) . "\n");
245 76         352 _write_inline_data($writer, $dataset_objects);
246 76         269 };
247 76         154 my @commands = ($plotter);
248 76         215 return $self->run_with(
249             do => \@commands,
250             _pair_slice(\%args, qw(writer async output no_stderr on_exit))
251             );
252             }
253              
254             sub _pair_slice {
255 106     106   269 my ($hash_ref, @keys) = @_;
256 106 100       189 return map { exists($hash_ref->{$_}) ? ($_ => $hash_ref->{$_}) : () } @keys;
  530         1178  
257             }
258              
259             sub plot_with {
260 21     21 1 1636 my ($self, %args) = @_;
261 21         104 return $self->_draw_with(%args, command => "plot");
262             }
263              
264             sub splot_with {
265 6     6 1 3068 my ($self, %args) = @_;
266 6         25 return $self->_draw_with(%args, command => "splot");
267             }
268              
269             sub plot {
270 46     46 1 294 my ($self, @dataset) = @_;
271 46         117 return $self->_draw_with(command => "plot", dataset => \@dataset);
272             }
273              
274             sub splot {
275 3     3 1 11 my ($self, @dataset) = @_;
276 3         9 return $self->_draw_with(command => "splot", dataset => \@dataset);
277             }
278              
279             sub multiplot_with {
280 30     30 1 1167 my ($self, %args) = @_;
281 30         54 my $do = $args{do};
282 30 50       67 croak "do parameter is mandatory" if not defined $do;
283 30 50       74 croak "do parameter must be a code-ref" if ref($do) ne "CODE";
284             my $wrapped_do = sub {
285 30     30   34 my $writer = shift;
286 30         65 my ($wrapped_writer, $data_written_ref, $ended_with_newline_ref) =
287             _wrap_writer_to_detect_empty_data($writer);
288 30         78 $do->($wrapped_writer);
289 29 100 100     3203 if($$data_written_ref && !$$ended_with_newline_ref) {
290 1         3 $writer->("\n");
291             }
292 30         88 };
293             my $multiplot_command =
294 30 100 100     104 (defined($args{option}) && $args{option} ne "")
295             ? "set multiplot $args{option}" : "set multiplot";
296 30         88 my @commands = ($multiplot_command, $wrapped_do, "unset multiplot");
297 30         73 return $self->run_with(
298             do => \@commands,
299             _pair_slice(\%args, qw(writer async output no_stderr on_exit))
300             );
301             }
302              
303             sub multiplot {
304 3     3 1 15 my ($self, $option, $code) = @_;
305 3 100       9 if(@_ == 2) {
306 2         3 $code = $option;
307 2         3 $option = "";
308             }
309 3 50       9 croak "code parameter is mandatory" if not defined $code;
310 3 50       10 croak "code parameter must be a code-ref" if ref($code) ne "CODE";
311 3         11 return $self->multiplot_with(do => $code, option => $option);
312             }
313              
314             our $_context_writer = undef;
315              
316             sub run_with {
317 159     159 1 10935 my ($self, %args) = @_;
318 159         312 my $commands = $args{do};
319 159 100       494 if(!defined($commands)) {
    100          
320 2         7 $commands = [];
321             }elsif(ref($commands) ne "ARRAY") {
322 28         86 $commands = [$commands];
323             }
324 159         256 my $on_exit = $args{on_exit};
325 159 50 33     396 if(defined($on_exit) && ref($on_exit) ne "CODE") {
326 0         0 croak "on_exit must be a CODE-REF";
327             }
328 159         422 _wrap_commands_with_output($commands, $self->_plotting_option(\%args, "output"));
329             my $do = sub {
330 159     159   216 my $writer = shift;
331 159 100 100     523 (!defined($_context_writer) || refaddr($_context_writer) != refaddr($writer))
332             and local $_context_writer = $writer;
333            
334 159         395 $writer->($self->to_string);
335 159         669 foreach my $command (@$commands) {
336 264 100       693 if(ref($command) eq "CODE") {
337 155         296 $command->($writer);
338             }else {
339 109         165 $command = "$command";
340 109         271 $writer->($command);
341 109 100       510 $writer->("\n") if $command !~ /\n$/;
342             }
343             }
344 159         617 };
345              
346 159         243 my $result = "";
347 159         287 my $got_writer = $self->_plotting_option(\%args, "writer");
348 159 100       321 if(defined($got_writer)) {
    50          
349 96 50       182 if(defined($on_exit)) {
350 0         0 croak "on_exit should not be set if writer is set";
351             }
352 96         169 $do->($got_writer);
353             }elsif(defined($_context_writer)) {
354 63 50       108 if(defined($on_exit)) {
355 0         0 croak "on_exit should not be set if there is a writer in the context"
356             }
357 63         120 $do->($_context_writer);
358             }else {
359 0         0 $result = Gnuplot::Builder::Process->with_new_process(
360             async => $self->_plotting_option(\%args, "async"),
361             do => $do,
362             no_stderr => $self->_plotting_option(\%args, "no_stderr"),
363             on_exit => $on_exit,
364             );
365             }
366 155         4478 return $result;
367             }
368              
369             sub _plotting_option {
370 318     318   560 my ($self, $given_args_ref, $key) = @_;
371             return (exists $given_args_ref->{$key})
372 318 100       789 ? $given_args_ref->{$key}
373             : $self->get_plot($key);
374             }
375              
376             sub run {
377 13     13 1 81 my ($self, @commands) = @_;
378 13         31 return $self->run_with(do => \@commands);
379             }
380              
381             my %KNOWN_PLOTTING_OPTIONS = map { ($_ => 1) } qw(output no_stderr writer async);
382              
383             sub _check_plotting_option {
384 266     266   402 my ($arg_name) = @_;
385 266 100       1134 croak "Unknown plotting option: $arg_name" if !$KNOWN_PLOTTING_OPTIONS{$arg_name};
386             }
387              
388             sub set_plot {
389 13     13 1 196 my ($self, %opts) = @_;
390 13         184 foreach my $key (keys %opts) {
391 16         47 _check_plotting_option($key);
392             $self->{pdata}->set_attribute(
393             key => $key,
394 15         67 value => $opts{$key}
395             );
396             }
397 12         42 return $self;
398             }
399              
400             sub get_plot {
401 242     242 1 1125 my ($self, $arg_name) = @_;
402 242         549 _check_plotting_option($arg_name);
403 241 50       477 croak "arg_name cannot be undef" if not defined $arg_name;
404 241         645 return $self->{pdata}->get_resolved_attribute($arg_name);
405             }
406              
407             sub delete_plot {
408 6     6 1 717 my ($self, @arg_names) = @_;
409 6         14 foreach my $arg_name (@arg_names) {
410 8         27 _check_plotting_option($arg_name);
411 7         30 $self->{pdata}->delete_attribute($arg_name)
412             }
413 5         20 return $self;
414             }
415              
416             sub Lens {
417 0     0 0   my ($self, $key) = @_;
418 0           require Gnuplot::Builder::Lens;
419 0           return Gnuplot::Builder::Lens->new(
420             "get_option", "set_option", $key
421             );
422             }
423              
424             1;
425              
426             __END__