File Coverage

lib/Google/RestApi/SheetsApi4/Worksheet.pm
Criterion Covered Total %
statement 163 239 68.2
branch 23 60 38.3
condition 14 25 56.0
subroutine 44 60 73.3
pod 34 45 75.5
total 278 429 64.8


line stmt bran cond sub pod time code
1             package Google::RestApi::SheetsApi4::Worksheet;
2              
3             our $VERSION = '1.0.3';
4              
5 1     1   588 use Google::RestApi::Setup;
  1         2  
  1         7  
6              
7 1     1   13975 use List::MoreUtils qw( first_index );
  1         10124  
  1         8  
8              
9 1     1   1113 use aliased 'Google::RestApi::SheetsApi4';
  1         3  
  1         11  
10 1     1   217 use aliased 'Google::RestApi::SheetsApi4::Range';
  1         3  
  1         4  
11 1     1   136 use aliased 'Google::RestApi::SheetsApi4::Range::Col';
  1         3  
  1         8  
12 1     1   233 use aliased 'Google::RestApi::SheetsApi4::Range::Row';
  1         2  
  1         5  
13 1     1   179 use aliased 'Google::RestApi::SheetsApi4::Range::Cell';
  1         2  
  1         5  
14 1     1   168 use aliased 'Google::RestApi::SheetsApi4::Range::All';
  1         3  
  1         4  
15 1     1   108 use aliased 'Google::RestApi::SheetsApi4::RangeGroup::Tie';
  1         3  
  1         5  
16 1     1   124 use aliased 'Google::RestApi::SheetsApi4::RangeGroup::Iterator';
  1         3  
  1         4  
17              
18 1     1   183 use parent 'Google::RestApi::SheetsApi4::Request::Spreadsheet::Worksheet';
  1         3  
  1         4  
19              
20             sub new {
21 118     118 1 9402 my $class = shift;
22              
23 118         502 my $qr_worksheet_uri = SheetsApi4->Worksheet_Uri;
24 118         266 state $check = compile_named(
25             spreadsheet => HasApi,
26             id => Str, { optional => 1 }, # the worksheet id (1, 2, 3 etc).
27             name => Str, { optional => 1 }, # the name of the worksheet.
28             title => Str, { optional => 1 },
29             uri => StrMatch[qr|$qr_worksheet_uri|], { optional => 1 },
30             );
31 118         7079 my $self = $check->(@_);
32 118         5159 $self = bless $self, $class;
33              
34 118   66     880 $self->{name} ||= $self->{title};
35 118         415 delete $self->{title};
36              
37             defined $self->{id} || defined $self->{name} || $self->{uri}
38 118 100 100     609 or LOGDIE "At least one of id, name, or uri must be specified";
      100        
39              
40 117         473 return $self->spreadsheet()->_register_worksheet($self);
41             }
42              
43             # work out the id from the uri or the name.
44             sub worksheet_id {
45 223     223 1 413 my $self = shift;
46 223 100       659 if (!defined $self->{id}) {
47 1 50       7 if ($self->{uri}) {
48 1         5 my $qr_worksheet_uri = SheetsApi4->Worksheet_Uri;
49 1         38 ($self->{id}) = $self->{uri} =~ m|$qr_worksheet_uri|;
50 1 50       8 LOGDIE "Unable to extract a worksheet id from URI '$self->{uri}'" if !defined $self->{id};
51             } else {
52 0         0 my $worksheets = $self->spreadsheet()->worksheet_properties('(title,sheetId)'); # potential recursion if $self->properties()
53 0         0 my ($worksheet) = grep { $_->{title} eq $self->{name}; } @$worksheets;
  0         0  
54 0 0       0 $worksheet or LOGDIE "Worksheet '$self->{name}' not found";
55 0         0 $self->{id} = $worksheet->{sheetId};
56             }
57 1         9 DEBUG("Got worksheet id '$self->{id}'");
58             }
59 223         1381 return $self->{id};
60             }
61              
62             sub worksheet_name {
63 1016     1016 1 1771 my $self = shift;
64 1016   66     2939 $self->{name} //= $self->properties('title')->{title};
65 1016         3189 return $self->{name};
66             }
67 0     0 0 0 sub worksheet_title { worksheet_name(@_); }
68              
69             # https://docs.google.com/spreadsheets/d/spreadsheetId/edit#gid=0
70             sub worksheet_uri {
71 0     0 1 0 my $self = shift;
72 0 0       0 if (!$self->{uri}) {
73 0         0 my $id = $self->worksheet_id();
74 0         0 $self->{uri} = "/edit#gid=$id";
75             }
76 0         0 return $self->spreadsheet()->spreadsheet_uri() . "/$self->{uri}";
77             }
78              
79             sub properties {
80 116     116 1 221 my $self = shift;
81 116         223 my $what = shift;
82 116         360 my $id = $self->worksheet_id();
83 116         324 my $worksheets = $self->spreadsheet()->worksheet_properties("($what,sheetId)");
84 116         418 my ($worksheet) = grep { $_->{sheetId} eq $id; } @$worksheets;
  116         522  
85 116 50       358 $worksheet or LOGDIE "Worksheet '$id' not found";
86 116         623 return $worksheet;
87             }
88              
89             # the following don't return ranges and don't use any batch, they are immediate.
90             # first arg is a range in any format. allow the range_col call to verify it.
91             sub col {
92 3     3 1 58 my $self = shift;
93 3         12 state $check = compile(Defined, ArrayRef[Str], { optional => 1 }); # A or 1
94 3         3461 my ($col, $values) = $check->(@_);
95 3         46 my $range = $self->range_col($col);
96 2 100       13 return $range->values(defined $values ? (values => $values) : ());
97             }
98              
99             sub cols {
100 3     3 1 3543 my $self = shift;
101              
102 3         11 state $check = compile(ArrayRef[Defined], ArrayRef[ArrayRef[Str]], { optional => 1 });
103 3         4440 my ($cols, $values) = $check->(@_);
104              
105 3         57 my $range_group = $self->range_group_cols($cols);
106 2 100       95 return $range_group->values() if !$values;
107              
108 1         6 my @ranges = $range_group->ranges();
109 1         5 foreach my $i (0..$#ranges) {
110 3         14 $ranges[$i]->batch_values(
111             values => $values->[$i],
112             );
113             }
114              
115 1         7 return $range_group->submit_values();
116             }
117              
118             sub row {
119 3     3 1 58 my $self = shift;
120 3         11 state $check = compile(Defined, ArrayRef[Str], { optional => 1 });
121 3         3415 my ($row, $values) = $check->(@_);
122 3         40 my $range = $self->range_row($row);
123 2 100       13 return $range->values(defined $values ? (values => $values) : ());
124             }
125              
126             sub rows {
127 3     3 1 944 my $self = shift;
128              
129 3         11 state $check = compile(ArrayRef[Defined], ArrayRef[ArrayRef[Str]], { optional => 1 });
130 3         4242 my ($rows, $values) = $check->(@_);
131              
132 3         59 my $range_group = $self->range_group_rows($rows);
133 2 100       100 return $range_group->values() if !$values;
134              
135 1         6 my @ranges = $range_group->ranges();
136 1         6 foreach my $i (0..$#ranges) {
137 3         15 $ranges[$i]->batch_values(
138             values => $values->[$i],
139             );
140             }
141              
142 1         7 return $range_group->submit_values();
143             }
144              
145             sub cell {
146 48     48 1 158 my $self = shift;
147 48         129 state $check = compile(Defined, Str, { optional => 1 });
148 48         2713 my ($cell, $value) = $check->(@_);
149 48         605 my $range = $self->range_cell($cell);
150 48 50       260 return $range->values(defined $value ? (values => $value) : ());
151             }
152              
153             sub cells {
154 0     0 0 0 my $self = shift;
155              
156 0         0 state $check = compile(ArrayRef[Defined], ArrayRef[Str], { optional => 1 });
157 0         0 my ($cells, $values) = $check->(@_);
158              
159 0         0 my $range_group = $self->range_group_cells($cells);
160 0 0       0 return $range_group->values() if !$values;
161              
162 0         0 my @ranges = $range_group->ranges();
163 0         0 foreach my $i (0..$#ranges) {
164 0         0 $ranges[$i]->batch_values(
165             values => $values->[$i],
166             );
167             }
168              
169 0         0 return $range_group->submit_values();
170             }
171              
172             # the 'ranges' key is poorly named since we can also submit requests for
173             # worksheets and spreadsheets. minor since this is all internal anyway.
174             sub submit_requests {
175 0     0 1 0 my $self = shift;
176 0         0 return $self->spreadsheet()->submit_requests(ranges => [ $self ], @_);
177             }
178              
179             # this is used by range to see if there is a match for a header col or row.
180             sub resolve_header_range {
181 1     1 0 3 my $self = shift;
182             return
183 1   33     5 $self->resolve_header_range_col(@_) ||
184             $self->resolve_header_range_row(@_);
185             }
186              
187             sub resolve_header_range_col {
188 1     1 0 2 my $self = shift;
189              
190 1         6 state $check = compile(RangeNamed);
191 1         2480 my ($header) = $check->(@_);
192            
193 1 50       21 my $headers = $self->header_col() or return;
194 0     0   0 my $i = first_index { $_ eq $header; } @$headers;
  0         0  
195 0 0       0 return [{col => 2, row => $i}, {row => $i}] if ++$i > 0;
196              
197 0         0 return;
198             }
199              
200             sub resolve_header_range_row {
201 1     1 0 3 my $self = shift;
202              
203 1         4 state $check = compile(RangeNamed);
204 1         2539 my ($header) = $check->(@_);
205            
206 1 50       21 my $headers = $self->header_row() or return;
207 0     0   0 my $i = first_index { $_ eq $header; } @$headers;
  0         0  
208 0 0       0 return [{col => $i, row => 2}, {col => $i}] if ++$i > 0;
209              
210 0         0 return;
211             }
212              
213             # call this before calling tie_rows or header_col.
214             # ('i really want to do this') turns it on, (false) turns it off.
215             # you must pass 'i really want to do this' to enable it. this is because you
216             # may have a worksheet with thousands of rows that end up being 'headers'.
217             # this is less of an issue with header row.
218             sub enable_header_col {
219 0     0 1 0 my $self = shift;
220 0   0     0 my $enable = shift // 1;
221 0 0       0 if ($enable =~ qr/i really want to do this/i) {
    0          
222 0         0 $self->{header_col_enabled} = 1;
223             } elsif ($enable) {
224 0         0 LOGDIE("You must enable header column by passing 'I really want to do this'");
225             } else {
226 0         0 delete @{$self}{qw(header_col header_col_enabled)};
  0         0  
227             }
228 0         0 return $enable;
229             }
230              
231             # call with true to refresh.
232             sub header_col {
233 1     1 1 3 my $self = shift;
234 1         2 my $refresh = shift;
235              
236 1 50       7 if (!$self->{header_col_enabled}) {
237 1         7 DEBUG("Header column is not enabled, call 'enable_header_col' first.");
238 1         12 delete $self->{header_col};
239 1         9 return;
240             }
241              
242 0 0       0 delete $self->{header_col} if $refresh;
243              
244 0 0       0 if (!$self->{header_col}) {
245 0         0 $self->{header_col} = $self->col(1);
246 0         0 DEBUG("Header col found:\n", Dump($self->{header_col}));
247             }
248              
249 0         0 return $self->{header_col};
250             }
251              
252             # call this before calling tie_cols (to use headings) or header_row.
253             # () or (true) turns it on, (false) turns it off.
254             sub enable_header_row {
255 3     3 1 20 my $self = shift;
256 3   50     17 my $enable = shift // 1;
257 3 50       12 if ($enable) {
258 3         11 $self->{header_row_enabled} = 1;
259             } else {
260 0         0 delete @{$self}{qw(header_row header_row_enabled)};
  0         0  
261             }
262 3         10 return $enable;
263             }
264              
265             # call with 1 to refresh.
266             sub header_row {
267 1     1 1 4 my $self = shift;
268 1         2 my $refresh = shift;
269              
270 1 50       5 if (!$self->{header_row_enabled}) {
271 1         5 DEBUG("Header row is not enabled, call 'enable_header_row' first.");
272 1         9 delete $self->{header_row};
273 1         9 return;
274             }
275              
276 0 0       0 delete $self->{header_row} if $refresh;
277              
278 0 0       0 if (!$self->{header_row}) {
279 0         0 $self->{header_row} = $self->row(1);
280 0         0 DEBUG("Header row found:\n", Dump($self->{header_row}));
281             }
282 0         0 return $self->{header_row};
283             }
284              
285 0     0 0 0 sub header_col_enabled { shift->{header_col_enabled}; }
286 0     0 0 0 sub header_row_enabled { shift->{header_row_enabled}; }
287              
288             sub normalize_named {
289 1     1 0 2 my $self = shift;
290              
291 1         5 state $check = compile(RangeNamed);
292 1         2542 my ($named_range_name) = $check->(@_);
293              
294 1         21 my ($sheet_id, $range) = $self->spreadsheet()->normalize_named($named_range_name);
295 1         6 my $this_sheet_id = $self->worksheet_id();
296 1 50 33     5 LOGDIE "Named range '$named_range_name' sheet ID is '$sheet_id', this sheet ID is '$this_sheet_id'"
297             if $sheet_id && $sheet_id != $this_sheet_id;
298              
299 1         5 return $range;
300             }
301              
302             # create a hash of name => value pairs from two columns in a worksheet.
303             sub name_value_pairs {
304 0     0 1 0 my $self = shift;
305              
306 0         0 state $check = compile(
307             Defined, { default => 1 }, # column of names (hash keys).
308             Defined, { default => 2 }, # column of values.
309             );
310 0         0 my ($name_col, $value_col) = $check->(@_);
311              
312 0         0 my $cols = $self->cols([$name_col, $value_col]);
313             my %pairs = map {
314 0 0       0 defined $cols->[0]->[$_]
315             ?
316             ( strip($cols->[0]->[$_]) => strip($cols->[1]->[$_]) )
317             :
318             ();
319 0 0       0 } (($self->header_row_enabled() ? 1 : 0)..$#{ $cols->[0] });
  0         0  
320              
321 0         0 return \%pairs;
322             }
323              
324 3     3 1 28 sub tie_cols { shift->_tie('range_col', @_); }
325 1     1 1 10 sub tie_rows { shift->_tie('range_row', @_); }
326 3     3 1 16 sub tie_cells { shift->_tie('range_cell', @_); }
327 4     4 1 11 sub tie_ranges { shift->_tie('range_factory', @_); }
328              
329             sub _tie {
330 11     11   34 my $self = shift;
331              
332             state $check = compile(
333 11 50 33 11   30 Str->where( sub { /^range/ && $self->can($_) or die "Must be a 'range' method"; } ),
  11         336  
334             slurpy HashRef,
335             );
336 11         3647 my ($method, $ranges) = $check->(@_);
337              
338 11         333 my %ranges = map { $_ => $self->$method( $ranges->{$_} ); } keys %$ranges;
  25         117  
339 11         68 return $self->tie(%ranges);
340             }
341              
342             sub tie {
343 15     15 1 47 my $self = shift;
344 15         52 my $tie = $self->spreadsheet()->tie(@_);
345 15         82 tied(%$tie)->default_worksheet($self);
346 15         131 return $tie;
347             }
348              
349             sub range_group_cols {
350 3     3 1 7 my $self = shift;
351 3         9 state $check = compile(ArrayRef[Defined]);
352 3         2266 my ($cols) = $check->(@_);
353 3         37 my @cols = map { $self->range_col($_); } @$cols;
  7         21  
354 2         8 return $self->spreadsheet()->range_group(@cols);
355             }
356              
357             sub range_group_rows {
358 3     3 1 9 my $self = shift;
359 3         10 state $check = compile(ArrayRef[Defined]);
360 3         2295 my ($rows) = $check->(@_);
361 3         37 my @rows = map { $self->range_row($_); } @$rows;
  7         25  
362 2         11 return $self->spreadsheet()->range_group(@rows);
363             }
364              
365             sub range_group_cells {
366 0     0 1 0 my $self = shift;
367 0         0 state $check = compile(ArrayRef[Defined]);
368 0         0 my ($cells) = $check->(@_);
369 0         0 my @cells = map { $self->range_cell($_); } @$cells;
  0         0  
370 0         0 return $self->spreadsheet()->range_group(@cells);
371             }
372              
373             # an arbitrary mix of ranges.
374             sub range_group {
375 0     0 1 0 my $self = shift;
376 0         0 state $check = compile(ArrayRef[Defined]);
377 0         0 my ($ranges) = $check->(@_);
378 0         0 my @ranges = map { $self->range_factory($_); } @$ranges;
  0         0  
379 0         0 return $self->spreadsheet()->range_group(@ranges);
380             }
381              
382             # can't use aliased here for some reason.
383 59     59 0 356 sub range_factory { Google::RestApi::SheetsApi4::Range::factory(worksheet => shift, range => shift, @_); }
384              
385 43     43 1 175 sub range { shift->range_factory(@_); }
386             # create these spcific subclasses so that invalid ranges will get caught.
387 32     32 1 264 sub range_col { Col->new(worksheet => shift, range => shift); }
388 28     28 1 241 sub range_row { Row->new(worksheet => shift, range => shift); }
389 74     74 1 497 sub range_cell { Cell->new(worksheet => shift, range => shift); }
390 0     0 1 0 sub range_all { All->new(worksheet => shift, @_); }
391 55     55 1 150 sub api { shift->spreadsheet()->api(@_); }
392 0     0 1 0 sub sheets_api { shift->spreadsheet()->sheets_api(@_); }
393 7     7 0 99 sub rest_api { shift->spreadsheet()->rest_api(@_); }
394 322     322 1 1575 sub spreadsheet { shift->{spreadsheet}; }
395 0     0 1   sub spreadsheet_id { shift->spreadsheet()->spreadsheet_id(); }
396 0     0 0   sub transaction { shift->spreadsheet()->transaction(); }
397              
398             1;
399              
400             __END__
401              
402             =head1 NAME
403              
404             Google::RestApi::SheetsApi4::Worksheet - Represents a Worksheet within a Google Spreadsheet.
405              
406             =head1 DESCRIPTION
407              
408             See the description and synopsis at L<Google::RestApi::SheetsApi4>.
409              
410             =head1 NAVIGATION
411              
412             =over
413              
414             =item * L<Google::RestApi::SheetsApi4>
415              
416             =item * L<Google::RestApi::SheetsApi4::Spreadsheet>
417              
418             =item * L<Google::RestApi::SheetsApi4::Worksheet>
419              
420             =item * L<Google::RestApi::SheetsApi4::Range>
421              
422             =item * L<Google::RestApi::SheetsApi4::Range::All>
423              
424             =item * L<Google::RestApi::SheetsApi4::Range::Col>
425              
426             =item * L<Google::RestApi::SheetsApi4::Range::Row>
427              
428             =item * L<Google::RestApi::SheetsApi4::Range::Cell>
429              
430             =item * L<Google::RestApi::SheetsApi4::RangeGroup>
431              
432             =item * L<Google::RestApi::SheetsApi4::RangeGroup::Iterator>
433              
434             =item * L<Google::RestApi::SheetsApi4::RangeGroup::Tie>
435              
436             =item * L<Google::RestApi::SheetsApi4::RangeGroup::Tie::Iterator>
437              
438             =item * L<Google::RestApi::SheetsApi4::Request::Spreadsheet>
439              
440             =item * L<Google::RestApi::SheetsApi4::Request::Spreadsheet::Worksheet>
441              
442             =item * L<Google::RestApi::SheetsApi4::Request::Spreadsheet::Worksheet::Range>
443              
444             =back
445              
446             =head1 SUBROUTINES
447              
448             =over
449              
450             =item new(spreadsheet => <object>, (id => <string> | name => <string> | uri => <string>));
451              
452             Creates a new instance of a Worksheet object. You would not normally
453             call this directly, you would obtain it from the
454             Spreadsheet->open_worksheet routine.
455              
456             spreadsheet: The parent object that represents the collection of worksheets.
457             id: The id of the worksheet (0, 1, 2 etc).
458             name: The name of the worksheet (as shown on the tab).
459             uri: The worksheet ID extracted from the overall URI.
460              
461             Only one of id/name/uri should be specified and this API will derive the others
462             as necessary.
463              
464             =item worksheet_id()
465              
466             Returns the worksheet id.
467              
468             =item worksheet_name()
469              
470             Returns the worksheet name or title.
471              
472             =item worksheet_uri()
473              
474             Returns the worksheet URL (URL);
475              
476             =item properties(what<string>);
477              
478             Returns the specific properties of this worksheet, such as the title or sheet id (sheetId).
479              
480             =item col(range<range>, values<arrayref>);
481              
482             Positional args consist of:
483              
484             =over
485              
486             =item * C<<range>>: A range representing a column.
487              
488             =item * C<<arrayref<str>>>: An array of strings of column values.
489              
490             =back
491              
492             Gets or sets the column values.
493              
494             $ws->col('A', [1, 2, 3]);
495             $values = $ws->col('A');
496              
497             Note: the Google API is called immediately, so this is the easiest but least efficient way of getting/setting spreadsheet values.
498              
499             Returns the values for the specified column.
500              
501             =item cols(cols, values); Positional args consist of:
502              
503             =over
504              
505             =item C<<arrayref<range>>>: An array of ranges that represent columns.
506              
507             =item C<<arrayref<arrayref<string>>>>: An optional array of values to set the columns to.
508              
509             =back
510              
511             Gets or sets a group of columns, see note for 'col' above.
512              
513             $ws->cols(['A', 2, 'Id'], [[1, 2, 3, 4], [5], [6, 7]]);
514             $values = $ws->cols(['A', 2, 'Id']);
515              
516             Returns the values for the specified columns.
517              
518             =item row(range<range>, values<arrayref<string>>);
519              
520             Same as 'col' above, but operates on a row.
521              
522             =item rows(rows<arrayref<range>>, values<arrayref<arrayref<string>>>)
523              
524             Same as 'cols' above, but operates on rows.
525              
526             =item cell(col<range>, row<range>|range<range>), value<string>);
527              
528             Same as above, but operates on a cell.
529              
530             =item enable_header_row(enable<boolean>)
531              
532             This turns on/off the header row so that column headings can be used as keys to tied hashes. See header_row.
533              
534             =item header_row(refresh<boolean>)
535              
536             Returns an array of values in the first row that act as simple
537             headers for the columns. The values are cached in the worksheet
538             so that multiple calls will only invoke the API once, unless you
539             pass a true value to the routine to refresh them.
540              
541             This is used internally to check for indexed column names such as
542             'Id', 'Name' or 'Address' etc. It may be of limited use externally.
543              
544             This will only work on simple headers that don't use fancy formatting
545             spread over multiple merged cells/rows.
546              
547             =item enable_header_col(enable<boolean>)
548              
549             This turns on/off the header row so that column headings can be used as keys to tied hashes. See header_col.
550              
551             You must pass C<i really want to do this> to turn it on. This is because you may have a worksheet with thousands of rows that end up being 'headers'. This is less of an issue with header row.
552              
553             =item header_col(refresh<boolean>)
554              
555             A less practical version of header_row, uses the first column to
556             label each row. Since this is cached, if the spreadsheet is large,
557             this can potentially use a lot of memory. Therefore, you must call
558             enable_header_col first, with C<i really want to do this> value, to obtain these column values.
559              
560             =item name_value_pairs(name_col<range>, value_col<range>);
561              
562             A utility to convert two columns into a simple hash.
563              
564             name_col: A range pointing to the keys of the hash.
565             value_col: A range pointing to the values of the hash.
566              
567             A spreadsheet with the values:
568              
569             Name Value
570             Fred 1
571             Charlie 2
572            
573             ...will return the hash:
574              
575             Fred => 1,
576             Charlie => 2,
577              
578             This allows you to store and retrieve a hash with little muss or fuss.
579              
580             =item tie_ranges(ranges<array<hash|<string>>>...);
581              
582             Ties the given ranges into a tied range group. Specify
583             either a 'key => range<range>' or a plain <range<string>>.
584              
585             $tied = $ws->tie_ranges({id => 'A2'}, 'B2', 'A5:B6');
586             $tied->{id} = [['1001']];
587             $tied->{B2} = [['Herb Ellis']];
588             $tied->{A5:B6} = [[1, 2], [3, 4]];
589             tied(%$tied)->submit_values();
590              
591             If you need to represent the range as anything but a string, you must
592             specify the key=>range format ({id => [1, 2]} or {id => {col => 1, row => 1}}).
593              
594             =item tie_cols(ranges<array<range>>...);
595              
596             Same as tie_ranges (above), but ties Range::Col objects. Specify
597             range strings, or column headings to represent the columns.
598              
599             $tied = $ws->tie_cols({id => 'A'}, 'Name');
600             $tied->{id} = [1001, 1002, 1003];
601             $tied->{Name} = ['Herb Ellis', 'Bela Fleck', 'Freddie Mercury'];
602             tied(%$tied)->submit_values();
603              
604             =item tie_rows(ranges<array<range>>...);
605              
606             Same as tie_cols (above), but ties Range::Row objects.
607              
608             $tied = $ws->tie_rows({herb => 2}, {bela => 3});
609             $tied->{herb} = ['Herb Ellis'];
610             $tied->{bela} = ['Bela Fleck'];
611             tied(%$tied)->submit_values();
612              
613             To use row 'headings' as ranges (assuming 'Herb Ellis' is in column 1),
614             you must call 'enable_header_col' to enable the row headers first
615             (see below).
616              
617             =item tie_cells(ranges<array<range>>...);
618              
619             Same as above, but ties Range::Cell objects.
620              
621             $tied = $ws->tie_cells(qw({id => A2}, B2));
622             $tied->{id} = 1001;
623             $tied->{B2} = 'Herb Ellis';
624             tied(%$tied)->submit_values();
625              
626             =item tie(ranges<hash>);
627              
628             Ties the given 'key => range' pairs into a tied range group, and sets
629             the default worksheet, for any new keys later added, to this worksheet.
630              
631             $tied = $ws->tie(id => $range_cell);
632             $tied->{id} = 1001;
633             $teid->{B2} = 'Herb Ellis'; # autocreated for this worksheet.
634             tied(%$tied)->submit_values();
635              
636             New keys that are added later are assumed to address cells if there is
637             no ':' (A1), or a general range if a ':' is found (A1:B2). It is better
638             to explicitly set all the ranges you expect to use on the call to 'tie'
639             rather than auto-creating the ranges later to avoid unexpected behaviour.
640              
641             See also L<Google::RestApi::SheetsApi4::Spreadsheet> C<tie>.
642              
643             =item submit_requests(%args)
644              
645             Submits any outstanding requests (API batchRequests) for this worksheet.
646             %args are any args to be passed to the RestApi's 'api' routine (content,
647             params etc).
648              
649             =item range(range<range>);
650              
651             Returns a Range object or one of its subclasses (Col, Row etc) representing the passed range. If you specify a range that
652             represents a column (A:A), you will get back a Range::Col object. To guaranty a particular subclass returned, use the below routines (range_col,
653             range_row, etc).
654              
655             =item range_col(range<range>);
656              
657             Returns a L<Google::RestApi::SheetsApi4::Range::Col> object representing the passed range. If you pass a non-column range, your script will die.
658              
659             =item range_row(range<range>);
660              
661             Returns a L<Google::RestApi::SheetsApi4::Range::Row> object representing the passed range.
662              
663             =item range_cell(range<range>);
664              
665             Returns a L<Google::RestApi::SheetsApi4::Range::Cell> object representing the passed range.
666              
667             =item range_all();
668              
669             Returns a L<Google::RestApi::SheetsApi4::Range::All> object that represents the whole worksheet. Caution: this class has not been fully tested.
670              
671             =item range_group_cols
672              
673             Returns a L<Google::RestApi::SheetsApi4::RangeGroup> object that represents a group of columns.
674              
675             =item range_group_rows(ranges<arrayref>);
676              
677             Returns a L<Google::RestApi::SheetsApi4::RangeGroup> object that represents a group of rows.
678              
679             =item range_group_cells(ranges<arrayref>);
680              
681             Returns a L<Google::RestApi::SheetsApi4::RangeGroup> object that represents a group of cells.
682              
683             =item range_group(ranges<arrayref>);
684              
685             Returns a L<Google::RestApi::SheetsApi4::RangeGroup> object that represents a group of arbitrary ranges.
686              
687             =item api(%args);
688              
689             A passthrough to the parent Spreadsheet object's 'api' routine.
690              
691             =item sheets_api();
692              
693             Returns the SheetsApi4 object.
694              
695             =item spreadsheet();
696              
697             Returns the parent Spreadsheet object.
698              
699             =item spreadsheet_id();
700              
701             Returns the parent Spreadsheet id.
702              
703             =back
704              
705             =head1 AUTHORS
706              
707             =over
708              
709             =item
710              
711             Robin Murray mvsjes@cpan.org
712              
713             =back
714              
715             =head1 COPYRIGHT
716              
717             Copyright (c) 2021, Robin Murray. All rights reserved.
718              
719             This program is free software; you may redistribute it and/or modify it under the same terms as Perl itself.