File Coverage

blib/lib/Gnuplot/Builder/Script.pm
Criterion Covered Total %
statement 224 228 98.2
branch 67 80 83.7
condition 11 15 73.3
subroutine 53 53 100.0
pod 26 26 100.0
total 381 402 94.7


line stmt bran cond sub pod time code
1             package Gnuplot::Builder::Script;
2 36     36   709159 use strict;
  36         81  
  36         984  
3 36     36   183 use warnings;
  36         65  
  36         972  
4 36     36   19636 use Gnuplot::Builder::PrototypedData;
  36         92  
  36         1169  
5 36     36   202 use Gnuplot::Builder::Util qw(quote_gnuplot_str);
  36         64  
  36         1584  
6 36     36   19895 use Gnuplot::Builder::Process;
  36         108  
  36         1327  
7 36     36   197 use Scalar::Util qw(weaken blessed refaddr);
  36         72  
  36         3029  
8 36     36   175 use Carp;
  36         60  
  36         2129  
9 36     36   56967 use overload '""' => "to_string";
  36         39559  
  36         211  
10              
11             sub new {
12 1174     1174 1 47483 my ($class, @set_args) = @_;
13 1174         2927 my $self = bless {
14             pdata => undef,
15             parent => undef,
16             };
17 1174         2512 $self->_init_pdata();
18 1174 100       2531 if(@set_args) {
19 18         71 $self->set(@set_args);
20             }
21 1174         2863 return $self;
22             }
23              
24             sub _init_pdata {
25 1174     1174   1491 my ($self) = @_;
26 1174         2516 weaken $self;
27             $self->{pdata} = Gnuplot::Builder::PrototypedData->new(
28             entry_evaluator => sub {
29 41     41   71 my ($key, $value_code) = @_;
30 41 100       82 if(defined($key)) {
31 38         119 return $value_code->($self, substr($key, 1));
32             }else {
33 3         8 return $value_code->($self);
34             }
35             }
36 1174         6224 );
37             }
38              
39             sub add {
40 15     15 1 60 my ($self, @sentences) = @_;
41 15         29 foreach my $sentence (@sentences) {
42 20         78 $self->{pdata}->add_entry($sentence);
43             }
44 15         38 return $self;
45             }
46              
47             sub _set_entry {
48 111     111   270 my ($self, $prefix, $quote, @pairs) = @_;
49             $self->{pdata}->set_entry(
50 111         549 entries => \@pairs,
51             key_prefix => $prefix,
52             quote => $quote,
53             );
54 111         837 return $self;
55             }
56              
57             sub set {
58 75     75 1 809 my ($self, @pairs) = @_;
59 75         239 return $self->_set_entry("o", 0, @pairs);
60             }
61              
62             *set_option = *set;
63              
64             sub setq {
65 21     21 1 112 my ($self, @pairs) = @_;
66 21         55 return $self->_set_entry("o", 1, @pairs);
67             }
68              
69             *setq_option = *setq;
70              
71             sub unset {
72 2     2 1 14 my ($self, @names) = @_;
73 2         5 return $self->set(map { $_ => undef } @names);
  4         12  
74             }
75              
76             sub _get_entry {
77 75     75   102 my ($self, $prefix, $name) = @_;
78 75 50       179 croak "name cannot be undef" if not defined $name;
79 75         291 return $self->{pdata}->get_resolved_entry("$prefix$name");
80             }
81              
82             sub get_option {
83 62     62 1 185 my ($self, $name) = @_;
84 62         141 return $self->_get_entry("o", $name);
85             }
86              
87             sub _delete_entry {
88 9     9   20 my ($self, $prefix, @names) = @_;
89 9         28 foreach my $name (@names) {
90 11 50       35 croak "name cannot be undef" if not defined $name;
91 11         53 $self->{pdata}->delete_entry("$prefix$name");
92             }
93 9         32 return $self;
94             }
95              
96             sub delete_option {
97 5     5 1 17 my ($self, @names) = @_;
98 5         20 return $self->_delete_entry("o", @names);
99             }
100              
101             sub _create_statement {
102 258     258   422 my ($raw_key, $value) = @_;
103 258 100       598 return $value if !defined $raw_key;
104 213         472 my ($prefix, $name) = (substr($raw_key, 0, 1), substr($raw_key, 1));
105 213         330 my @words = ();
106 213 100       477 if($prefix eq "o") {
    50          
107 177 100       591 @words = defined($value) ? ("set", $name, $value) : ("unset", $name);
108             }elsif($prefix eq "d") {
109 36 100       112 @words = defined($value) ? ($name, "=", $value) : ("undefine", $name);
110             }else {
111 0         0 confess "Unknown key prefix: $prefix";
112             }
113 213         373 return join(" ", grep { "$_" ne "" } @words);
  611         1657  
114             }
115              
116             sub to_string {
117 231     231 1 1757 my ($self) = @_;
118 231         323 my $result = "";
119             $self->{pdata}->each_resolved_entry(sub {
120 246     246   5985 my ($raw_key, $values) = @_;
121 246         435 foreach my $value (@$values) {
122 258         532 my $statement = _create_statement($raw_key, $value);
123 258         481 $result .= $statement;
124 258 100       1756 $result .= "\n" if $statement !~ /\n$/;
125             }
126 231         1365 });
127 231         1907 return $result;
128             }
129              
130             sub define {
131 15     15 1 74 my ($self, @pairs) = @_;
132 15         53 return $self->_set_entry("d", 0, @pairs);
133             }
134              
135             *set_definition = *define;
136              
137             sub undefine {
138 1     1 1 6 my ($self, @names) = @_;
139 1         3 return $self->define(map { $_ => undef } @names);
  3         8  
140             }
141              
142             sub get_definition {
143 13     13 1 27 my ($self, $name) = @_;
144 13         30 return $self->_get_entry("d", $name);
145             }
146              
147             sub delete_definition {
148 4     4 1 11 my ($self, @names) = @_;
149 4         16 return $self->_delete_entry("d", @names);
150             }
151              
152             sub set_parent {
153 1011     1011 1 1321 my ($self, $parent) = @_;
154 1011 100       1870 if(!defined($parent)) {
155 1         2 $self->{parent} = undef;
156 1         5 $self->{pdata}->set_parent(undef);
157 1         4 return $self;
158             }
159 1010 50 33     5029 if(!blessed($parent) || !$parent->isa("Gnuplot::Builder::Script")) {
160 0         0 croak "parent must be a Gnuplot::Builder::Script"
161             }
162 1010         1680 $self->{parent} = $parent;
163 1010         2835 $self->{pdata}->set_parent($parent->{pdata});
164 1010         2181 return $self;
165             }
166              
167 5     5 1 33 sub get_parent { return $_[0]->{parent} }
168              
169             *parent = *get_parent;
170              
171             sub new_child {
172 1008     1008 1 3355 my ($self) = @_;
173 1008         2013 return Gnuplot::Builder::Script->new->set_parent($self);
174             }
175              
176             sub _collect_dataset_params {
177 74     74   114 my ($dataset_arrayref) = @_;
178 74         107 my @params_str = ();
179 74         107 my @dataset_objects = ();
180 74         124 foreach my $dataset (@$dataset_arrayref) {
181 89         121 my $ref = ref($dataset);
182 89 100       171 if(!$ref) {
183 70         157 push(@params_str, $dataset);
184             }else {
185 19 50 33     166 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         65 my ($param_str) = $dataset->params_string();
189 19         2352 push(@params_str, $param_str);
190 19         51 push(@dataset_objects, $dataset);
191             }
192             }
193 74         173 return (\@params_str, \@dataset_objects);
194             }
195              
196             sub _wrap_writer_to_detect_empty_data {
197 103     103   152 my ($writer) = @_;
198 103         129 my $ended_with_newline = 0;
199 103         123 my $data_written = 0;
200             my $wrapped_writer = sub {
201 37 100   37   209 my @nonempty_data = grep { defined($_) && $_ ne "" } @_;
  35         218  
202 37 100       95 return if !@nonempty_data;
203 30         44 $data_written = 1;
204 30         97 $ended_with_newline = ($nonempty_data[-1] =~ /\n$/);
205 30         108 $writer->(join("", @nonempty_data));
206 103         356 };
207 103         248 return ($wrapped_writer, \$data_written, \$ended_with_newline);
208             }
209              
210             sub _write_inline_data {
211 74     74   109 my ($writer, $dataset_objects_arrayref) = @_;
212 74         143 my ($wrapped_writer, $data_written_ref, $ended_with_newline_ref) =
213             _wrap_writer_to_detect_empty_data($writer);
214 74         421 foreach my $dataset (@$dataset_objects_arrayref) {
215 19         33 $$data_written_ref = $$ended_with_newline_ref = 0;
216 19         59 $dataset->write_data_to($wrapped_writer);
217 19 100       106 next if !$$data_written_ref;
218 11 100       35 $writer->("\n") if !$$ended_with_newline_ref;
219 11         36 $writer->("e\n");
220             }
221             }
222              
223             sub _wrap_commands_with_output {
224 156     156   236 my ($commands_ref, $output_filename) = @_;
225 156 100       386 if(defined($output_filename)) {
226 12         53 unshift @$commands_ref, "set output " . quote_gnuplot_str($output_filename);
227 12         33 push @$commands_ref, "set output";
228             }
229             }
230              
231             sub _draw_with {
232 74     74   215 my ($self, %args) = @_;
233 74         157 my $plot_command = $args{command};
234 74         105 my $dataset = $args{dataset};
235 74 50       174 croak "dataset parameter is mandatory" if not defined $dataset;
236 74 100       230 if(ref($dataset) ne "ARRAY") {
237 19         37 $dataset = [$dataset];
238             }
239 74 50       170 croak "at least one dataset is required" if !@$dataset;
240              
241             my $plotter = sub {
242 74     74   112 my $writer = shift;
243 74         162 my ($params, $dataset_objects) = _collect_dataset_params($dataset);
244 74         356 $writer->("$plot_command " . join(",", @$params) . "\n");
245 74         341 _write_inline_data($writer, $dataset_objects);
246 74         257 };
247 74         148 my @commands = ($plotter);
248 74         194 return $self->run_with(
249             do => \@commands,
250             _pair_slice(\%args, qw(writer async output no_stderr))
251             );
252             }
253              
254             sub _pair_slice {
255 103     103   217 my ($hash_ref, @keys) = @_;
256 103 100       157 return map { exists($hash_ref->{$_}) ? ($_ => $hash_ref->{$_}) : () } @keys;
  412         1194  
257             }
258              
259             sub plot_with {
260 21     21 1 1074 my ($self, %args) = @_;
261 21         79 return $self->_draw_with(%args, command => "plot");
262             }
263              
264             sub splot_with {
265 6     6 1 1546 my ($self, %args) = @_;
266 6         22 return $self->_draw_with(%args, command => "splot");
267             }
268              
269             sub plot {
270 44     44 1 259 my ($self, @dataset) = @_;
271 44         115 return $self->_draw_with(command => "plot", dataset => \@dataset);
272             }
273              
274             sub splot {
275 3     3 1 11 my ($self, @dataset) = @_;
276 3         11 return $self->_draw_with(command => "splot", dataset => \@dataset);
277             }
278              
279             sub multiplot_with {
280 29     29 1 765 my ($self, %args) = @_;
281 29         50 my $do = $args{do};
282 29 50       75 croak "do parameter is mandatory" if not defined $do;
283 29 50       81 croak "do parameter must be a code-ref" if ref($do) ne "CODE";
284             my $wrapped_do = sub {
285 29     29   41 my $writer = shift;
286 29         55 my ($wrapped_writer, $data_written_ref, $ended_with_newline_ref) =
287             _wrap_writer_to_detect_empty_data($writer);
288 29         81 $do->($wrapped_writer);
289 28 100 100     3127 if($$data_written_ref && !$$ended_with_newline_ref) {
290 1         3 $writer->("\n");
291             }
292 29         95 };
293             my $multiplot_command =
294 29 100 100     132 (defined($args{option}) && $args{option} ne "")
295             ? "set multiplot $args{option}" : "set multiplot";
296 29         74 my @commands = ($multiplot_command, $wrapped_do, "unset multiplot");
297 29         75 return $self->run_with(
298             do => \@commands,
299             _pair_slice(\%args, qw(writer async output no_stderr))
300             );
301             }
302              
303             sub multiplot {
304 3     3 1 14 my ($self, $option, $code) = @_;
305 3 100       11 if(@_ == 2) {
306 2         3 $code = $option;
307 2         4 $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         13 return $self->multiplot_with(do => $code, option => $option);
312             }
313              
314             our $_context_writer = undef;
315              
316             sub run_with {
317 156     156 1 6077 my ($self, %args) = @_;
318 156         252 my $commands = $args{do};
319 156 100       542 if(!defined($commands)) {
    100          
320 2         4 $commands = [];
321             }elsif(ref($commands) ne "ARRAY") {
322 28         57 $commands = [$commands];
323             }
324 156         369 _wrap_commands_with_output($commands, $self->_plotting_option(\%args, "output"));
325             my $do = sub {
326 156     156   224 my $writer = shift;
327 156 100 100     731 (!defined($_context_writer) || refaddr($_context_writer) != refaddr($writer))
328             and local $_context_writer = $writer;
329            
330 156         380 $writer->($self->to_string);
331 156         665 foreach my $command (@$commands) {
332 259 100       753 if(ref($command) eq "CODE") {
333 152         314 $command->($writer);
334             }else {
335 107         164 $command = "$command";
336 107         223 $writer->($command);
337 107 100       570 $writer->("\n") if $command !~ /\n$/;
338             }
339             }
340 156         601 };
341              
342 156         249 my $result = "";
343 156         353 my $got_writer = $self->_plotting_option(\%args, "writer");
344 156 100       368 if(defined($got_writer)) {
    50          
345 95         204 $do->($got_writer);
346             }elsif(defined($_context_writer)) {
347 61         122 $do->($_context_writer);
348             }else {
349 0         0 $result = Gnuplot::Builder::Process->with_new_process(
350             async => $self->_plotting_option(\%args, "async"),
351             do => $do,
352             no_stderr => $self->_plotting_option(\%args, "no_stderr")
353             );
354             }
355 152         4458 return $result;
356             }
357              
358             sub _plotting_option {
359 312     312   481 my ($self, $given_args_ref, $key) = @_;
360             return (exists $given_args_ref->{$key})
361 312 100       969 ? $given_args_ref->{$key}
362             : $self->get_plot($key);
363             }
364              
365             sub run {
366 13     13 1 78 my ($self, @commands) = @_;
367 13         33 return $self->run_with(do => \@commands);
368             }
369              
370             my %KNOWN_PLOTTING_OPTIONS = map { ($_ => 1) } qw(output no_stderr writer async);
371              
372             sub _check_plotting_option {
373 261     261   352 my ($arg_name) = @_;
374 261 100       1076 croak "Unknown plotting option: $arg_name" if !$KNOWN_PLOTTING_OPTIONS{$arg_name};
375             }
376              
377             sub set_plot {
378 13     13 1 111 my ($self, %opts) = @_;
379 13         31 foreach my $key (keys %opts) {
380 16         28 _check_plotting_option($key);
381             $self->{pdata}->set_attribute(
382             key => $key,
383 15         60 value => $opts{$key}
384             );
385             }
386 12         38 return $self;
387             }
388              
389             sub get_plot {
390 237     237 1 866 my ($self, $arg_name) = @_;
391 237         429 _check_plotting_option($arg_name);
392 236 50       476 croak "arg_name cannot be undef" if not defined $arg_name;
393 236         1326 return $self->{pdata}->get_resolved_attribute($arg_name);
394             }
395              
396             sub delete_plot {
397 6     6 1 451 my ($self, @arg_names) = @_;
398 6         14 foreach my $arg_name (@arg_names) {
399 8         14 _check_plotting_option($arg_name);
400 7         25 $self->{pdata}->delete_attribute($arg_name)
401             }
402 5         16 return $self;
403             }
404              
405             1;
406              
407             __END__